summaryrefslogtreecommitdiff
path: root/drivers/mxc/security
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mxc/security')
-rw-r--r--drivers/mxc/security/Kconfig64
-rw-r--r--drivers/mxc/security/Makefile11
-rw-r--r--drivers/mxc/security/dryice-regs.h207
-rw-r--r--drivers/mxc/security/dryice.c1123
-rw-r--r--drivers/mxc/security/dryice.h287
-rw-r--r--drivers/mxc/security/mxc_scc.c2386
-rw-r--r--drivers/mxc/security/mxc_scc_internals.h498
-rw-r--r--drivers/mxc/security/rng/Makefile35
-rw-r--r--drivers/mxc/security/rng/des_key.c385
-rw-r--r--drivers/mxc/security/rng/fsl_shw_hash.c84
-rw-r--r--drivers/mxc/security/rng/fsl_shw_hmac.c83
-rw-r--r--drivers/mxc/security/rng/fsl_shw_rand.c122
-rw-r--r--drivers/mxc/security/rng/fsl_shw_sym.c317
-rw-r--r--drivers/mxc/security/rng/fsl_shw_wrap.c1301
-rw-r--r--drivers/mxc/security/rng/include/rng_driver.h134
-rw-r--r--drivers/mxc/security/rng/include/rng_internals.h680
-rw-r--r--drivers/mxc/security/rng/include/rng_rnga.h181
-rw-r--r--drivers/mxc/security/rng/include/rng_rngc.h235
-rw-r--r--drivers/mxc/security/rng/include/shw_driver.h2971
-rw-r--r--drivers/mxc/security/rng/include/shw_hash.h96
-rw-r--r--drivers/mxc/security/rng/include/shw_hmac.h82
-rw-r--r--drivers/mxc/security/rng/include/shw_internals.h162
-rw-r--r--drivers/mxc/security/rng/rng_driver.c1150
-rw-r--r--drivers/mxc/security/rng/shw_driver.c2335
-rw-r--r--drivers/mxc/security/rng/shw_dryice.c204
-rw-r--r--drivers/mxc/security/rng/shw_hash.c328
-rw-r--r--drivers/mxc/security/rng/shw_hmac.c145
-rw-r--r--drivers/mxc/security/rng/shw_memory_mapper.c213
-rw-r--r--drivers/mxc/security/sahara2/Kconfig35
-rw-r--r--drivers/mxc/security/sahara2/Makefile47
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_auth.c706
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_hash.c186
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_hmac.c266
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_keystore.c837
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_rand.c96
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_sym.c281
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_user.c137
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_wrap.c967
-rw-r--r--drivers/mxc/security/sahara2/include/adaptor.h113
-rw-r--r--drivers/mxc/security/sahara2/include/diagnostic.h116
-rw-r--r--drivers/mxc/security/sahara2/include/fsl_platform.h161
-rw-r--r--drivers/mxc/security/sahara2/include/fsl_shw.h2515
-rw-r--r--drivers/mxc/security/sahara2/include/fsl_shw_keystore.h475
-rw-r--r--drivers/mxc/security/sahara2/include/linux_port.h1806
-rw-r--r--drivers/mxc/security/sahara2/include/platform_abstractions.h15
-rw-r--r--drivers/mxc/security/sahara2/include/portable_os.h1453
-rw-r--r--drivers/mxc/security/sahara2/include/sah_driver_common.h102
-rw-r--r--drivers/mxc/security/sahara2/include/sah_hardware_interface.h99
-rw-r--r--drivers/mxc/security/sahara2/include/sah_interrupt_handler.h42
-rw-r--r--drivers/mxc/security/sahara2/include/sah_kernel.h113
-rw-r--r--drivers/mxc/security/sahara2/include/sah_memory_mapper.h79
-rw-r--r--drivers/mxc/security/sahara2/include/sah_queue_manager.h63
-rw-r--r--drivers/mxc/security/sahara2/include/sah_status_manager.h228
-rw-r--r--drivers/mxc/security/sahara2/include/sahara.h2265
-rw-r--r--drivers/mxc/security/sahara2/include/sahara2_kernel.h49
-rw-r--r--drivers/mxc/security/sahara2/include/sf_util.h466
-rw-r--r--drivers/mxc/security/sahara2/km_adaptor.c849
-rw-r--r--drivers/mxc/security/sahara2/sah_driver_interface.c2185
-rw-r--r--drivers/mxc/security/sahara2/sah_hardware_interface.c808
-rw-r--r--drivers/mxc/security/sahara2/sah_interrupt_handler.c216
-rw-r--r--drivers/mxc/security/sahara2/sah_memory_mapper.c2356
-rw-r--r--drivers/mxc/security/sahara2/sah_queue.c249
-rw-r--r--drivers/mxc/security/sahara2/sah_queue_manager.c1076
-rw-r--r--drivers/mxc/security/sahara2/sah_status_manager.c714
-rw-r--r--drivers/mxc/security/sahara2/sf_util.c1390
-rw-r--r--drivers/mxc/security/scc2_driver.c2401
-rw-r--r--drivers/mxc/security/scc2_internals.h519
67 files changed, 42300 insertions, 0 deletions
diff --git a/drivers/mxc/security/Kconfig b/drivers/mxc/security/Kconfig
new file mode 100644
index 000000000000..875848b2c69c
--- /dev/null
+++ b/drivers/mxc/security/Kconfig
@@ -0,0 +1,64 @@
+menu "MXC Security Drivers"
+
+config MXC_SECURITY_SCC
+ tristate "MXC SCC Driver"
+ default n
+ ---help---
+ This module contains the core API's for accessing the SCC module.
+ If you are unsure about this, say N here.
+
+config MXC_SECURITY_SCC2
+ tristate "MXC SCC2 Driver"
+ depends on ARCH_MX37 || ARCH_MX5
+ default n
+ ---help---
+ This module contains the core API's for accessing the SCC2 module.
+ If you are unsure about this, say N here.
+
+config SCC_DEBUG
+ bool "MXC SCC Module debugging"
+ depends on MXC_SECURITY_SCC || MXC_SECURITY_SCC2
+ ---help---
+ This is an option for use by developers; most people should
+ say N here. This enables SCC module debugging.
+
+config MXC_SECURITY_RNG
+ tristate "MXC RNG Driver"
+ depends on ARCH_MXC
+ depends on !ARCH_MXC91321
+ depends on !ARCH_MX27
+ default n
+ select MXC_SECURITY_CORE
+ ---help---
+ This module contains the core API's for accessing the RNG module.
+ If you are unsure about this, say N here.
+
+config MXC_RNG_TEST_DRIVER
+ bool "MXC RNG debug register"
+ depends on MXC_SECURITY_RNG
+ default n
+ ---help---
+ This option enables the RNG kcore driver to provide peek-poke facility
+ into the RNG device registers. Enable this, only for development and
+ testing purposes.
+config MXC_RNG_DEBUG
+ bool "MXC RNG Module Dubugging"
+ depends on MXC_SECURITY_RNG
+ default n
+ ---help---
+ This is an option for use by developers; most people should
+ say N here. This enables RNG module debugging.
+
+config MXC_DRYICE
+ tristate "MXC DryIce Driver"
+ depends on ARCH_MX25
+ default n
+ ---help---
+ This module contains the core API's for accessing the DryIce module.
+ If you are unsure about this, say N here.
+
+if (ARCH_MX37 || ARCH_MX5 || ARCH_MX27)
+source "drivers/mxc/security/sahara2/Kconfig"
+endif
+
+endmenu
diff --git a/drivers/mxc/security/Makefile b/drivers/mxc/security/Makefile
new file mode 100644
index 000000000000..f643a09a423d
--- /dev/null
+++ b/drivers/mxc/security/Makefile
@@ -0,0 +1,11 @@
+# Makefile for the Linux MXC Security API
+ifeq ($( SCC_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+EXTRA_CFLAGS += -DMXC -DLINUX_KERNEL
+
+obj-$(CONFIG_MXC_SECURITY_SCC2) += scc2_driver.o
+obj-$(CONFIG_MXC_SECURITY_SCC) += mxc_scc.o
+obj-$(CONFIG_MXC_SECURITY_RNG) += rng/
+obj-$(CONFIG_MXC_SAHARA) += sahara2/
+obj-$(CONFIG_MXC_DRYICE) += dryice.o
diff --git a/drivers/mxc/security/dryice-regs.h b/drivers/mxc/security/dryice-regs.h
new file mode 100644
index 000000000000..2da10585965a
--- /dev/null
+++ b/drivers/mxc/security/dryice-regs.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+
+#ifndef __DRYICE_REGS_H__
+#define __DRYICE_REGS_H__
+
+/***********************************************************************
+ * DryIce Register Definitions
+ ***********************************************************************/
+
+/* DryIce Time Counter MSB Reg */
+#define DTCMR 0x00
+
+/* DryIce Time Counter LSB Reg */
+#define DTCLR 0x04
+
+/* DryIce Clock Alarm MSB Reg */
+#define DCAMR 0x08
+
+/* DryIce Clock Alarm LSB Reg */
+#define DCALR 0x0c
+
+/* DryIce Control Reg */
+#define DCR 0x10
+#define DCR_TDCHL (1 << 30) /* Tamper Detect Config Hard Lock */
+#define DCR_TDCSL (1 << 29) /* Tamper Detect COnfig Soft Lock */
+#define DCR_KSHL (1 << 28) /* Key Select Hard Lock */
+#define DCR_KSSL (1 << 27) /* Key Select Soft Lock */
+#define DCR_RKHL (1 << 26) /* Random Key Hard Lock */
+#define DCR_RKSL (1 << 25) /* Random Key Soft Lock */
+#define DCR_PKRHL (1 << 24) /* Programmed Key Read Hard Lock */
+#define DCR_PKRSL (1 << 23) /* Programmed Key Read Soft Lock */
+#define DCR_PKWHL (1 << 22) /* Programmed Key Write Hard Lock */
+#define DCR_PKWSL (1 << 21) /* Programmed Key Write Soft Lock */
+#define DCR_MCHL (1 << 20) /* Monotonic Counter Hard Lock */
+#define DCR_MCSL (1 << 19) /* Monotonic Counter Soft Lock */
+#define DCR_TCHL (1 << 18) /* Time Counter Hard Lock */
+#define DCR_TCSL (1 << 17) /* Time Counter Soft Lock */
+#define DCR_FSHL (1 << 16) /* Failure State Hard Lock */
+#define DCR_NSA (1 << 15) /* Non-Secure Access */
+#define DCR_OSCB (1 << 14) /* Oscillator Bypass */
+#define DCR_APE (1 << 4) /* Alarm Pin Enable */
+#define DCR_TCE (1 << 3) /* Time Counter Enable */
+#define DCR_MCE (1 << 2) /* Monotonic Counter Enable */
+#define DCR_SWR (1 << 0) /* Software Reset (w/o) */
+
+/* DryIce Status Reg */
+#define DSR 0x14
+#define DSR_WTD (1 << 23) /* Wire-mesh Tampering Detected */
+#define DSR_ETBD (1 << 22) /* External Tampering B Detected */
+#define DSR_ETAD (1 << 21) /* External Tampering A Detected */
+#define DSR_EBD (1 << 20) /* External Boot Detected */
+#define DSR_SAD (1 << 19) /* Security Alarm Detected */
+#define DSR_TTD (1 << 18) /* Temperature Tampering Detected */
+#define DSR_CTD (1 << 17) /* Clock Tampering Detected */
+#define DSR_VTD (1 << 16) /* Voltage Tampering Detected */
+#define DSR_KBF (1 << 11) /* Key Busy Flag */
+#define DSR_WBF (1 << 10) /* Write Busy Flag */
+#define DSR_WNF (1 << 9) /* Write Next Flag */
+#define DSR_WCF (1 << 8) /* Write Complete Flag */
+#define DSR_WEF (1 << 7) /* Write Error Flag */
+#define DSR_RKE (1 << 6) /* Random Key Error */
+#define DSR_RKV (1 << 5) /* Random Key Valid */
+#define DSR_CAF (1 << 4) /* Clock Alarm Flag */
+#define DSR_MCO (1 << 3) /* Monotonic Counter Overflow */
+#define DSR_TCO (1 << 2) /* Time Counter Overflow */
+#define DSR_NVF (1 << 1) /* Non-Valid Flag */
+#define DSR_SVF (1 << 0) /* Security Violation Flag */
+
+#define DSR_TAMPER_BITS (DSR_WTD | DSR_ETBD | DSR_ETAD | DSR_EBD | DSR_SAD | \
+ DSR_TTD | DSR_CTD | DSR_VTD | DSR_MCO | DSR_TCO)
+
+/* ensure that external tamper defs match register bits */
+#if DSR_WTD != DI_TAMPER_EVENT_WTD
+#error "Mismatch between DSR_WTD and DI_TAMPER_EVENT_WTD"
+#endif
+#if DSR_ETBD != DI_TAMPER_EVENT_ETBD
+#error "Mismatch between DSR_ETBD and DI_TAMPER_EVENT_ETBD"
+#endif
+#if DSR_ETAD != DI_TAMPER_EVENT_ETAD
+#error "Mismatch between DSR_ETAD and DI_TAMPER_EVENT_ETAD"
+#endif
+#if DSR_EBD != DI_TAMPER_EVENT_EBD
+#error "Mismatch between DSR_EBD and DI_TAMPER_EVENT_EBD"
+#endif
+#if DSR_SAD != DI_TAMPER_EVENT_SAD
+#error "Mismatch between DSR_SAD and DI_TAMPER_EVENT_SAD"
+#endif
+#if DSR_TTD != DI_TAMPER_EVENT_TTD
+#error "Mismatch between DSR_TTD and DI_TAMPER_EVENT_TTD"
+#endif
+#if DSR_CTD != DI_TAMPER_EVENT_CTD
+#error "Mismatch between DSR_CTD and DI_TAMPER_EVENT_CTD"
+#endif
+#if DSR_VTD != DI_TAMPER_EVENT_VTD
+#error "Mismatch between DSR_VTD and DI_TAMPER_EVENT_VTD"
+#endif
+#if DSR_MCO != DI_TAMPER_EVENT_MCO
+#error "Mismatch between DSR_MCO and DI_TAMPER_EVENT_MCO"
+#endif
+#if DSR_TCO != DI_TAMPER_EVENT_TCO
+#error "Mismatch between DSR_TCO and DI_TAMPER_EVENT_TCO"
+#endif
+
+/* DryIce Interrupt Enable Reg */
+#define DIER 0x18
+#define DIER_WNIE (1 << 9) /* Write Next Interrupt Enable */
+#define DIER_WCIE (1 << 8) /* Write Complete Interrupt Enable */
+#define DIER_WEIE (1 << 7) /* Write Error Interrupt Enable */
+#define DIER_RKIE (1 << 5) /* Random Key Interrupt Enable */
+#define DIER_CAIE (1 << 4) /* Clock Alarm Interrupt Enable */
+#define DIER_MOIE (1 << 3) /* Monotonic Overflow Interrupt En */
+#define DIER_TOIE (1 << 2) /* Time Overflow Interrupt Enable */
+#define DIER_SVIE (1 << 0) /* Security Violation Interrupt En */
+
+/* DryIce Monotonic Counter Reg */
+#define DMCR 0x1c
+
+/* DryIce Key Select Reg */
+#define DKSR 0x20
+#define DKSR_IIM_KEY 0x0
+#define DKSR_PROG_KEY 0x4
+#define DKSR_RAND_KEY 0x5
+#define DKSR_PROG_XOR_IIM_KEY 0x6
+#define DKSR_RAND_XOR_IIM_KEY 0x7
+
+/* DryIce Key Control Reg */
+#define DKCR 0x24
+#define DKCR_LRK (1 << 0) /* Load Random Key */
+
+/* DryIce Tamper Configuration Reg */
+#define DTCR 0x28
+#define DTCR_ETGFB_SHIFT 27 /* Ext Tamper Glitch Filter B */
+#define DTCR_ETGFB_MASK 0xf8000000
+#define DTCR_ETGFA_SHIFT 22 /* Ext Tamper Glitch Filter A */
+#define DTCR_ETGFA_MASK 0x07c00000
+#define DTCR_WTGF_SHIFT 17 /* Wire-mesh Tamper Glitch Filter */
+#define DTCR_WTGF_MASK 0x003e0000
+#define DTCR_WGFE (1 << 16) /* Wire-mesh Glitch Filter Enable */
+#define DTCR_SAOE (1 << 15) /* Security Alarm Output Enable */
+#define DTCR_MOE (1 << 9) /* Monotonic Overflow Enable */
+#define DTCR_TOE (1 << 8) /* Time Overflow Enable */
+#define DTCR_WTE (1 << 7) /* Wire-mesh Tampering Enable */
+#define DTCR_ETBE (1 << 6) /* External Tampering B Enable */
+#define DTCR_ETAE (1 << 5) /* External Tampering A Enable */
+#define DTCR_EBE (1 << 4) /* External Boot Enable */
+#define DTCR_SAIE (1 << 3) /* Security Alarm Input Enable */
+#define DTCR_TTE (1 << 2) /* Temperature Tamper Enable */
+#define DTCR_CTE (1 << 1) /* Clock Tamper Enable */
+#define DTCR_VTE (1 << 0) /* Voltage Tamper Enable */
+
+/* DryIce Analog Configuration Reg */
+#define DACR 0x2c
+#define DACR_VRC_SHIFT 6 /* Voltage Reference Configuration */
+#define DACR_VRC_MASK 0x000001c0
+#define DACR_HTDC_SHIFT 3 /* High Temperature Detect Configuration */
+#define DACR_HTDC_MASK 0x00000038
+#define DACR_LTDC_SHIFT 0 /* Low Temperature Detect Configuration */
+#define DACR_LTDC_MASK 0x00000007
+
+/* DryIce General Purpose Reg */
+#define DGPR 0x3c
+
+/* DryIce Programmed Key0-7 Regs */
+#define DPKR0 0x40
+#define DPKR1 0x44
+#define DPKR2 0x48
+#define DPKR3 0x4c
+#define DPKR4 0x50
+#define DPKR5 0x54
+#define DPKR6 0x58
+#define DPKR7 0x5c
+
+/* DryIce Random Key0-7 Regs */
+#define DRKR0 0x60
+#define DRKR1 0x64
+#define DRKR2 0x68
+#define DRKR3 0x6c
+#define DRKR4 0x70
+#define DRKR5 0x74
+#define DRKR6 0x78
+#define DRKR7 0x7c
+
+#define DI_ADDRESS_RANGE (DRKR7 + 4)
+
+/*
+ * this doesn't really belong here but the
+ * portability layer doesn't include it
+ */
+#ifdef LINUX_KERNEL
+#define EXTERN_SYMBOL(symbol) EXPORT_SYMBOL(symbol)
+#else
+#define EXTERN_SYMBOL(symbol) do {} while (0)
+#endif
+
+#endif /* __DRYICE_REGS_H__ */
diff --git a/drivers/mxc/security/dryice.c b/drivers/mxc/security/dryice.c
new file mode 100644
index 000000000000..7d0d45b71d83
--- /dev/null
+++ b/drivers/mxc/security/dryice.c
@@ -0,0 +1,1123 @@
+/*
+ * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+
+#undef DI_DEBUG /* enable debug messages */
+#undef DI_DEBUG_REGIO /* show register read/write */
+#undef DI_TESTING /* include test code */
+
+#ifdef DI_DEBUG
+#define di_debug(fmt, arg...) os_printk(KERN_INFO fmt, ##arg)
+#else
+#define di_debug(fmt, arg...) do {} while (0)
+#endif
+
+#define di_info(fmt, arg...) os_printk(KERN_INFO fmt, ##arg)
+#define di_warn(fmt, arg...) os_printk(KERN_WARNING fmt, ##arg)
+
+#include "sahara2/include/portable_os.h"
+#include "dryice.h"
+#include "dryice-regs.h"
+
+/* mask of the lock-related function flags */
+#define DI_FUNC_LOCK_FLAGS (DI_FUNC_FLAG_READ_LOCK | \
+ DI_FUNC_FLAG_WRITE_LOCK | \
+ DI_FUNC_FLAG_HARD_LOCK)
+
+/*
+ * dryice hardware states
+ */
+enum di_states {
+ DI_STATE_VALID = 0,
+ DI_STATE_NON_VALID,
+ DI_STATE_FAILURE,
+};
+
+/*
+ * todo list actions
+ */
+enum todo_actions {
+ TODO_ACT_WRITE_VAL,
+ TODO_ACT_WRITE_PTR,
+ TODO_ACT_WRITE_PTR32,
+ TODO_ACT_ASSIGN,
+ TODO_ACT_WAIT_RKG,
+};
+
+/*
+ * todo list status
+ */
+enum todo_status {
+ TODO_ST_LOADING,
+ TODO_ST_READY,
+ TODO_ST_PEND_WCF,
+ TODO_ST_PEND_RKG,
+ TODO_ST_DONE,
+};
+
+OS_DEV_INIT_DCL(dryice_init)
+OS_DEV_SHUTDOWN_DCL(dryice_exit)
+OS_DEV_ISR_DCL(dryice_norm_irq)
+OS_WAIT_OBJECT(done_queue);
+OS_WAIT_OBJECT(exit_queue);
+
+struct dryice_data {
+ int busy; /* enforce exclusive access */
+ os_lock_t busy_lock;
+ int exit_flag; /* don't start new operations */
+
+ uint32_t baseaddr; /* physical base address */
+ void *ioaddr; /* virtual base address */
+
+ /* interrupt handling */
+ struct irq_struct {
+ os_interrupt_id_t irq;
+ int set;
+ } irq_norm, irq_sec;
+
+ struct clk *clk; /* clock control */
+
+ int key_programmed; /* key has been programmed */
+ int key_selected; /* key has been selected */
+
+ /* callback function and cookie */
+ void (*cb_func)(di_return_t rc, unsigned long cookie);
+ unsigned long cb_cookie;
+} *di = NULL;
+
+#define TODO_LIST_LEN 12
+static struct {
+ struct td {
+ enum todo_actions action;
+ uint32_t src;
+ uint32_t dst;
+ int num;
+ } list[TODO_LIST_LEN];
+ int cur; /* current todo pointer */
+ int num; /* number of todo's on the list */
+ int async; /* non-zero if list is async */
+ int status; /* current status of the list */
+ di_return_t rc; /* return code generated by the list */
+} todo;
+
+/*
+ * dryice register read/write functions
+ */
+#ifdef DI_DEBUG_REGIO
+static uint32_t di_read(int reg)
+{
+ uint32_t val = os_read32(di->ioaddr + (reg));
+ di_info("di_read(0x%02x) = 0x%08x\n", reg, val);
+
+ return val;
+}
+
+static void di_write(uint32_t val, int reg)
+{
+ di_info("dryice_write_reg(0x%08x, 0x%02x)\n", val, reg);
+ os_write32(di->ioaddr + (reg), val);
+}
+#else
+#define di_read(reg) os_read32(di->ioaddr + (reg))
+#define di_write(val, reg) os_write32(di->ioaddr + (reg), val);
+#endif
+
+/*
+ * set the dryice busy flag atomically, allowing
+ * for case where the driver is trying to exit.
+ */
+static int di_busy_set(void)
+{
+ os_lock_context_t context;
+ int rc = 0;
+
+ os_lock_save_context(di->busy_lock, context);
+ if (di->exit_flag || di->busy)
+ rc = 1;
+ else
+ di->busy = 1;
+ os_unlock_restore_context(di->busy_lock, context);
+
+ return rc;
+}
+
+/*
+ * clear the dryice busy flag
+ */
+static inline void di_busy_clear(void)
+{
+ /* don't acquire the lock because the race is benign */
+ di->busy = 0;
+
+ if (di->exit_flag)
+ os_wake_sleepers(exit_queue);
+}
+
+/*
+ * return the current state of dryice
+ * (valid, non-valid, or failure)
+ */
+static enum di_states di_state(void)
+{
+ enum di_states state = DI_STATE_VALID;
+ uint32_t dsr = di_read(DSR);
+
+ if (dsr & DSR_NVF)
+ state = DI_STATE_NON_VALID;
+ else if (dsr & DSR_SVF)
+ state = DI_STATE_FAILURE;
+
+ return state;
+}
+
+#define DI_WRITE_LOOP_CNT 0x1000
+/*
+ * the write-error flag is something that shouldn't get set
+ * during normal operation. if it's set something is terribly
+ * wrong. the best we can do is try to clear the bit and hope
+ * that dryice will recover. this situation is similar to an
+ * unexpected bus fault in terms of severity.
+ */
+static void try_to_clear_wef(void)
+{
+ int cnt;
+
+ while (1) {
+ di_write(DSR_WEF, DSR);
+ for (cnt = 0; cnt < DI_WRITE_LOOP_CNT; cnt++) {
+ if ((di_read(DSR) & DSR_WEF) == 0)
+ break;
+ }
+ di_warn("WARNING: DryIce cannot clear DSR_WEF "
+ "(Write Error Flag)!\n");
+ }
+}
+
+/*
+ * write a dryice register and loop, waiting for it
+ * to complete. use only during driver initialization.
+ * returns 0 on success or 1 on write failure.
+ */
+static int di_write_loop(uint32_t val, int reg)
+{
+ int rc = 0;
+ int cnt;
+
+ di_debug("FUNC: %s\n", __func__);
+ di_write(val, reg);
+
+ for (cnt = 0; cnt < DI_WRITE_LOOP_CNT; cnt++) {
+ uint32_t dsr = di_read(DSR);
+ if (dsr & DSR_WEF) {
+ try_to_clear_wef();
+ rc = 1;
+ }
+ if (dsr & DSR_WCF)
+ break;
+ }
+ di_debug("wait_write_loop looped %d times\n", cnt);
+ if (cnt == DI_WRITE_LOOP_CNT)
+ rc = 1;
+
+ if (rc)
+ di_warn("DryIce wait_write_done: WRITE ERROR!\n");
+ return rc;
+}
+
+/*
+ * initialize the todo list. must be called
+ * before adding items to the list.
+ */
+static void todo_init(int async_flag)
+{
+ di_debug("FUNC: %s\n", __func__);
+ todo.cur = 0;
+ todo.num = 0;
+ todo.async = async_flag;
+ todo.rc = 0;
+ todo.status = TODO_ST_LOADING;
+}
+
+/*
+ * perform the current action on the todo list
+ */
+#define TC todo.list[todo.cur]
+void todo_cur(void)
+{
+ di_debug("FUNC: %s[%d]\n", __func__, todo.cur);
+ switch (TC.action) {
+ case TODO_ACT_WRITE_VAL:
+ di_debug(" TODO_ACT_WRITE_VAL\n");
+ /* enable the write-completion interrupt */
+ todo.status = TODO_ST_PEND_WCF;
+ di_write(di_read(DIER) | DIER_WCIE, DIER);
+
+ di_write(TC.src, TC.dst);
+ break;
+
+ case TODO_ACT_WRITE_PTR32:
+ di_debug(" TODO_ACT_WRITE_PTR32\n");
+ /* enable the write-completion interrupt */
+ todo.status = TODO_ST_PEND_WCF;
+ di_write(di_read(DIER) | DIER_WCIE, DIER);
+
+ di_write(*(uint32_t *)TC.src, TC.dst);
+ break;
+
+ case TODO_ACT_WRITE_PTR:
+ {
+ uint8_t *p = (uint8_t *)TC.src;
+ uint32_t val = 0;
+ int num = TC.num;
+
+ di_debug(" TODO_ACT_WRITE_PTR\n");
+ while (num--)
+ val = (val << 8) | *p++;
+
+ /* enable the write-completion interrupt */
+ todo.status = TODO_ST_PEND_WCF;
+ di_write(di_read(DIER) | DIER_WCIE, DIER);
+
+ di_write(val, TC.dst);
+ }
+ break;
+
+ case TODO_ACT_ASSIGN:
+ di_debug(" TODO_ACT_ASSIGN\n");
+ switch (TC.num) {
+ case 1:
+ *(uint8_t *)TC.dst = TC.src;
+ break;
+ case 2:
+ *(uint16_t *)TC.dst = TC.src;
+ break;
+ case 4:
+ *(uint32_t *)TC.dst = TC.src;
+ break;
+ default:
+ di_warn("Unexpected size in TODO_ACT_ASSIGN\n");
+ break;
+ }
+ break;
+
+ case TODO_ACT_WAIT_RKG:
+ di_debug(" TODO_ACT_WAIT_RKG\n");
+ /* enable the random-key interrupt */
+ todo.status = TODO_ST_PEND_RKG;
+ di_write(di_read(DIER) | DIER_RKIE, DIER);
+ break;
+
+ default:
+ di_debug(" TODO_ACT_NOOP\n");
+ break;
+ }
+}
+
+/*
+ * called when done with the todo list.
+ * if async, it does the callback.
+ * if blocking, it wakes up the caller.
+ */
+static void todo_done(di_return_t rc)
+{
+ todo.rc = rc;
+ todo.status = TODO_ST_DONE;
+ if (todo.async) {
+ di_busy_clear();
+ if (di->cb_func)
+ di->cb_func(rc, di->cb_cookie);
+ } else
+ os_wake_sleepers(done_queue);
+}
+
+/*
+ * performs the actions sequentially from the todo list
+ * until it encounters an item that isn't ready.
+ */
+static void todo_run(void)
+{
+ di_debug("FUNC: %s\n", __func__);
+ while (todo.status == TODO_ST_READY) {
+ if (todo.cur == todo.num) {
+ todo_done(0);
+ break;
+ }
+ todo_cur();
+ if (todo.status != TODO_ST_READY)
+ break;
+ todo.cur++;
+ }
+}
+
+/*
+ * kick off the todo list by making it ready
+ */
+static void todo_start(void)
+{
+ di_debug("FUNC: %s\n", __func__);
+ todo.status = TODO_ST_READY;
+ todo_run();
+}
+
+/*
+ * blocking callers sleep here until the todo list is done
+ */
+static int todo_wait_done(void)
+{
+ di_debug("FUNC: %s\n", __func__);
+ os_sleep(done_queue, todo.status == TODO_ST_DONE, 0);
+
+ return todo.rc;
+}
+
+/*
+ * add a dryice register write to the todo list.
+ * the value to be written is supplied.
+ */
+#define todo_write_val(val, reg) \
+ todo_add(TODO_ACT_WRITE_VAL, val, reg, 0)
+
+/*
+ * add a dryice register write to the todo list.
+ * "size" bytes pointed to by addr will be written.
+ */
+#define todo_write_ptr(addr, reg, size) \
+ todo_add(TODO_ACT_WRITE_PTR, (uint32_t)addr, reg, size)
+
+/*
+ * add a dryice register write to the todo list.
+ * the word pointed to by addr will be written.
+ */
+#define todo_write_ptr32(addr, reg) \
+ todo_add(TODO_ACT_WRITE_PTR32, (uint32_t)addr, reg, 0)
+
+/*
+ * add a dryice memory write to the todo list.
+ * object can only have a size of 1, 2, or 4 bytes.
+ */
+#define todo_assign(var, val) \
+ todo_add(TODO_ACT_ASSIGN, val, (uint32_t)&(var), sizeof(var))
+
+#define todo_wait_rkg() \
+ todo_add(TODO_ACT_WAIT_RKG, 0, 0, 0)
+
+static void todo_add(int action, uint32_t src, uint32_t dst, int num)
+{
+ struct td *p = &todo.list[todo.num];
+
+ di_debug("FUNC: %s\n", __func__);
+ if (todo.num == TODO_LIST_LEN) {
+ di_warn("WARNING: DryIce todo-list overflow!\n");
+ return;
+ }
+ p->action = action;
+ p->src = src;
+ p->dst = dst;
+ p->num = num;
+ todo.num++;
+}
+
+#if defined(DI_DEBUG) || defined(DI_TESTING)
+/*
+ * print out the contents of the dryice status register
+ * with all the bits decoded
+ */
+static void show_dsr(const char *heading)
+{
+ uint32_t dsr = di_read(DSR);
+
+ di_info("%s\n", heading);
+ if (dsr & DSR_TAMPER_BITS) {
+ if (dsr & DSR_WTD)
+ di_info("Wire-mesh Tampering Detected\n");
+ if (dsr & DSR_ETBD)
+ di_info("External Tampering B Detected\n");
+ if (dsr & DSR_ETAD)
+ di_info("External Tampering A Detected\n");
+ if (dsr & DSR_EBD)
+ di_info("External Boot Detected\n");
+ if (dsr & DSR_SAD)
+ di_info("Security Alarm Detected\n");
+ if (dsr & DSR_TTD)
+ di_info("Temperature Tampering Detected\n");
+ if (dsr & DSR_CTD)
+ di_info("Clock Tampering Detected\n");
+ if (dsr & DSR_VTD)
+ di_info("Voltage Tampering Detected\n");
+ if (dsr & DSR_MCO)
+ di_info("Monotonic Counter Overflow\n");
+ if (dsr & DSR_TCO)
+ di_info("Time Counter Overflow\n");
+ } else
+ di_info("No Tamper Events Detected\n");
+
+ di_info("%d Key Busy Flag\n", !!(dsr & DSR_KBF));
+ di_info("%d Write Busy Flag\n", !!(dsr & DSR_WBF));
+ di_info("%d Write Next Flag\n", !!(dsr & DSR_WNF));
+ di_info("%d Write Complete Flag\n", !!(dsr & DSR_WCF));
+ di_info("%d Write Error Flag\n", !!(dsr & DSR_WEF));
+ di_info("%d Random Key Error\n", !!(dsr & DSR_RKE));
+ di_info("%d Random Key Valid\n", !!(dsr & DSR_RKV));
+ di_info("%d Clock Alarm Flag\n", !!(dsr & DSR_CAF));
+ di_info("%d Non-Valid Flag\n", !!(dsr & DSR_NVF));
+ di_info("%d Security Violation Flag\n", !!(dsr & DSR_SVF));
+}
+
+/*
+ * print out a key in hex
+ */
+static void print_key(const char *tag, uint8_t *key, int bits)
+{
+ int bytes = (bits + 7) / 8;
+
+ di_info("%s", tag);
+ while (bytes--)
+ os_printk("%02x", *key++);
+ os_printk("\n");
+}
+#endif /* defined(DI_DEBUG) || defined(DI_TESTING) */
+
+/*
+ * dryice normal interrupt service routine
+ */
+OS_DEV_ISR(dryice_norm_irq)
+{
+ /* save dryice status register */
+ uint32_t dsr = di_read(DSR);
+
+ if (dsr & DSR_WCF) {
+ /* disable the write-completion interrupt */
+ di_write(di_read(DIER) & ~DIER_WCIE, DIER);
+
+ if (todo.status == TODO_ST_PEND_WCF) {
+ if (dsr & DSR_WEF) {
+ try_to_clear_wef();
+ todo_done(DI_ERR_WRITE);
+ } else {
+ todo.cur++;
+ todo.status = TODO_ST_READY;
+ todo_run();
+ }
+ }
+ } else if (dsr & (DSR_RKV | DSR_RKE)) {
+ /* disable the random-key-gen interrupt */
+ di_write(di_read(DIER) & ~DIER_RKIE, DIER);
+
+ if (todo.status == TODO_ST_PEND_RKG) {
+ if (dsr & DSR_RKE)
+ todo_done(DI_ERR_FAIL);
+ else {
+ todo.cur++;
+ todo.status = TODO_ST_READY;
+ todo_run();
+ }
+ }
+ }
+
+ os_dev_isr_return(1);
+}
+
+/* write loop with error handling -- for init only */
+#define di_write_loop_goto(val, reg, rc, label) \
+ do {if (di_write_loop(val, reg)) \
+ {rc = OS_ERROR_FAIL_S; goto label; } } while (0)
+
+/*
+ * dryice driver initialization
+ */
+OS_DEV_INIT(dryice_init)
+{
+ di_return_t rc = 0;
+
+ di_info("MXC DryIce driver\n");
+
+ /* allocate memory */
+ di = os_alloc_memory(sizeof(*di), GFP_KERNEL);
+ if (di == NULL) {
+ rc = OS_ERROR_NO_MEMORY_S;
+ goto err_alloc;
+ }
+ memset(di, 0, sizeof(*di));
+ di->baseaddr = DRYICE_BASE_ADDR;
+ di->irq_norm.irq = MXC_INT_DRYICE_NORM;
+ di->irq_sec.irq = MXC_INT_DRYICE_SEC;
+
+ /* map i/o registers */
+ di->ioaddr = os_map_device(di->baseaddr, DI_ADDRESS_RANGE);
+ if (di->ioaddr == NULL) {
+ rc = OS_ERROR_FAIL_S;
+ goto err_iomap;
+ }
+
+ /* allocate locks */
+ di->busy_lock = os_lock_alloc_init();
+ if (di->busy_lock == NULL) {
+ rc = OS_ERROR_NO_MEMORY_S;
+ goto err_locks;
+ }
+
+ /* enable clocks (is there a portable way to do this?) */
+ di->clk = clk_get(NULL, "dryice_clk");
+ clk_enable(di->clk);
+
+ /* register for interrupts */
+ /* os_register_interrupt() dosen't support an option to make the
+ interrupt as shared. Replaced it with request_irq().*/
+ rc = request_irq(di->irq_norm.irq, dryice_norm_irq, IRQF_SHARED,
+ "dry_ice", di);
+ if (rc)
+ goto err_irqs;
+ else
+ di->irq_norm.set = 1;
+
+ /*
+ * DRYICE HARDWARE INIT
+ */
+
+#ifdef DI_DEBUG
+ show_dsr("DSR Pre-Initialization State");
+#endif
+
+ if (di_state() == DI_STATE_NON_VALID) {
+ uint32_t dsr = di_read(DSR);
+
+ di_debug("initializing from non-valid state\n");
+
+ /* clear security violation flag */
+ if (dsr & DSR_SVF)
+ di_write_loop_goto(DSR_SVF, DSR, rc, err_write);
+
+ /* clear tamper detect flags */
+ if (dsr & DSR_TAMPER_BITS)
+ di_write_loop_goto(DSR_TAMPER_BITS, DSR, rc, err_write);
+
+ /* initialize timers */
+ di_write_loop_goto(0, DTCLR, rc, err_write);
+ di_write_loop_goto(0, DTCMR, rc, err_write);
+ di_write_loop_goto(0, DMCR, rc, err_write);
+
+ /* clear non-valid flag */
+ di_write_loop_goto(DSR_NVF, DSR, rc, err_write);
+ }
+
+ /* set tamper events we are interested in watching */
+ di_write_loop_goto(DTCR_WTE | DTCR_ETBE | DTCR_ETAE, DTCR, rc,
+ err_write);
+#ifdef DI_DEBUG
+ show_dsr("DSR Post-Initialization State");
+#endif
+ os_dev_init_return(OS_ERROR_OK_S);
+
+err_write:
+ /* unregister interrupts */
+ if (di->irq_norm.set)
+ os_deregister_interrupt(di->irq_norm.irq);
+ if (di->irq_sec.set)
+ os_deregister_interrupt(di->irq_sec.irq);
+
+ /* turn off clocks (is there a portable way to do this?) */
+ clk_disable(di->clk);
+ clk_put(di->clk);
+
+err_irqs:
+ /* unallocate locks */
+ os_lock_deallocate(di->busy_lock);
+
+err_locks:
+ /* unmap i/o registers */
+ os_unmap_device(di->ioaddr, DI_ADDRESS_RANGE);
+
+err_iomap:
+ /* free the dryice struct */
+ os_free_memory(di);
+
+err_alloc:
+ os_dev_init_return(rc);
+}
+
+/*
+ * dryice driver exit routine
+ */
+OS_DEV_SHUTDOWN(dryice_exit)
+{
+ /* don't allow new operations */
+ di->exit_flag = 1;
+
+ /* wait for the current operation to complete */
+ os_sleep(exit_queue, di->busy == 0, 0);
+
+ /* unregister interrupts */
+ if (di->irq_norm.set)
+ os_deregister_interrupt(di->irq_norm.irq);
+ if (di->irq_sec.set)
+ os_deregister_interrupt(di->irq_sec.irq);
+
+ /* turn off clocks (is there a portable way to do this?) */
+ clk_disable(di->clk);
+ clk_put(di->clk);
+
+ /* unallocate locks */
+ os_lock_deallocate(di->busy_lock);
+
+ /* unmap i/o registers */
+ os_unmap_device(di->ioaddr, DI_ADDRESS_RANGE);
+
+ /* free the dryice struct */
+ os_free_memory(di);
+
+ os_dev_shutdown_return(OS_ERROR_OK_S);
+}
+
+di_return_t dryice_set_programmed_key(const void *key_data, int key_bits,
+ int flags)
+{
+ uint32_t dcr;
+ int key_bytes, reg;
+ di_return_t rc = 0;
+
+ if (di_busy_set())
+ return DI_ERR_BUSY;
+
+ if (key_data == NULL) {
+ rc = DI_ERR_INVAL;
+ goto err;
+ }
+ if (key_bits < 0 || key_bits > MAX_KEY_LEN || key_bits % 8) {
+ rc = DI_ERR_INVAL;
+ goto err;
+ }
+ if (flags & DI_FUNC_FLAG_WORD_KEY) {
+ if (key_bits % 32 || (uint32_t)key_data & 0x3) {
+ rc = DI_ERR_INVAL;
+ goto err;
+ }
+ }
+ if (di->key_programmed) {
+ rc = DI_ERR_INUSE;
+ goto err;
+ }
+ if (di_state() == DI_STATE_FAILURE) {
+ rc = DI_ERR_STATE;
+ goto err;
+ }
+ dcr = di_read(DCR);
+ if (dcr & DCR_PKWHL) {
+ rc = DI_ERR_HLOCK;
+ goto err;
+ }
+ if (dcr & DCR_PKWSL) {
+ rc = DI_ERR_SLOCK;
+ goto err;
+ }
+ key_bytes = key_bits / 8;
+
+ todo_init((flags & DI_FUNC_FLAG_ASYNC) != 0);
+
+ /* accomodate busses that can only do 32-bit transfers */
+ if (flags & DI_FUNC_FLAG_WORD_KEY) {
+ uint32_t *keyp = (void *)key_data;
+
+ for (reg = 0; reg < MAX_KEY_WORDS; reg++) {
+ if (reg < MAX_KEY_WORDS - key_bytes / 4)
+ todo_write_val(0, DPKR7 - reg * 4);
+ else {
+ todo_write_ptr32(keyp, DPKR7 - reg * 4);
+ keyp++;
+ }
+ }
+ } else {
+ uint8_t *keyp = (void *)key_data;
+
+ for (reg = 0; reg < MAX_KEY_WORDS; reg++) {
+ int size = key_bytes - (MAX_KEY_WORDS - reg - 1) * 4;
+ if (size <= 0)
+ todo_write_val(0, DPKR7 - reg * 4);
+ else {
+ if (size > 4)
+ size = 4;
+ todo_write_ptr(keyp, DPKR7 - reg * 4, size);
+ keyp += size;
+ }
+ }
+ }
+ todo_assign(di->key_programmed, 1);
+
+ if (flags & DI_FUNC_LOCK_FLAGS) {
+ dcr = di_read(DCR);
+ if (flags & DI_FUNC_FLAG_READ_LOCK) {
+ if (flags & DI_FUNC_FLAG_HARD_LOCK)
+ dcr |= DCR_PKRHL;
+ else
+ dcr |= DCR_PKRSL;
+ }
+ if (flags & DI_FUNC_FLAG_WRITE_LOCK) {
+ if (flags & DI_FUNC_FLAG_HARD_LOCK)
+ dcr |= DCR_PKWHL;
+ else
+ dcr |= DCR_PKWSL;
+ }
+ todo_write_val(dcr, DCR);
+ }
+ todo_start();
+
+ if (flags & DI_FUNC_FLAG_ASYNC)
+ return 0;
+
+ rc = todo_wait_done();
+err:
+ di_busy_clear();
+ return rc;
+}
+EXTERN_SYMBOL(dryice_set_programmed_key);
+
+di_return_t dryice_get_programmed_key(uint8_t *key_data, int key_bits)
+{
+ int reg, byte, key_bytes;
+ uint32_t dcr, dpkr;
+ di_return_t rc = 0;
+
+ if (di_busy_set())
+ return DI_ERR_BUSY;
+
+ if (key_data == NULL) {
+ rc = DI_ERR_INVAL;
+ goto err;
+ }
+ if (key_bits < 0 || key_bits > MAX_KEY_LEN || key_bits % 8) {
+ rc = DI_ERR_INVAL;
+ goto err;
+ }
+ #if 0
+ if (!di->key_programmed) {
+ rc = DI_ERR_UNSET;
+ goto err;
+ }
+ #endif
+ if (di_state() == DI_STATE_FAILURE) {
+ rc = DI_ERR_STATE;
+ goto err;
+ }
+ dcr = di_read(DCR);
+ if (dcr & DCR_PKRHL) {
+ rc = DI_ERR_HLOCK;
+ goto err;
+ }
+ if (dcr & DCR_PKRSL) {
+ rc = DI_ERR_SLOCK;
+ goto err;
+ }
+ key_bytes = key_bits / 8;
+
+ /* read key */
+ for (reg = 0; reg < MAX_KEY_WORDS; reg++) {
+ if (reg < (MAX_KEY_BYTES - key_bytes) / 4)
+ continue;
+ dpkr = di_read(DPKR7 - reg * 4);
+
+ for (byte = 0; byte < 4; byte++) {
+ if (reg * 4 + byte >= MAX_KEY_BYTES - key_bytes) {
+ int shift = 24 - byte * 8;
+ *key_data++ = (dpkr >> shift) & 0xff;
+ }
+ }
+ dpkr = 0; /* cleared for security */
+ }
+err:
+ di_busy_clear();
+ return rc;
+}
+EXTERN_SYMBOL(dryice_get_programmed_key);
+
+di_return_t dryice_release_programmed_key(void)
+{
+ uint32_t dcr;
+ di_return_t rc = 0;
+
+ if (di_busy_set())
+ return DI_ERR_BUSY;
+
+ if (!di->key_programmed) {
+ rc = DI_ERR_UNSET;
+ goto err;
+ }
+ dcr = di_read(DCR);
+ if (dcr & DCR_PKWHL) {
+ rc = DI_ERR_HLOCK;
+ goto err;
+ }
+ if (dcr & DCR_PKWSL) {
+ rc = DI_ERR_SLOCK;
+ goto err;
+ }
+ di->key_programmed = 0;
+
+err:
+ di_busy_clear();
+ return rc;
+}
+EXTERN_SYMBOL(dryice_release_programmed_key);
+
+di_return_t dryice_set_random_key(int flags)
+{
+ uint32_t dcr;
+ di_return_t rc = 0;
+
+ if (di_busy_set())
+ return DI_ERR_BUSY;
+
+ if (di_state() == DI_STATE_FAILURE) {
+ rc = DI_ERR_STATE;
+ goto err;
+ }
+ dcr = di_read(DCR);
+ if (dcr & DCR_RKHL) {
+ rc = DI_ERR_HLOCK;
+ goto err;
+ }
+ if (dcr & DCR_RKSL) {
+ rc = DI_ERR_SLOCK;
+ goto err;
+ }
+ todo_init((flags & DI_FUNC_FLAG_ASYNC) != 0);
+
+ /* clear Random Key Error bit, if set */
+ if (di_read(DSR) & DSR_RKE)
+ todo_write_val(DSR_RKE, DCR);
+
+ /* load random key */
+ todo_write_val(DKCR_LRK, DKCR);
+
+ /* wait for RKV (valid) or RKE (error) */
+ todo_wait_rkg();
+
+ if (flags & DI_FUNC_LOCK_FLAGS) {
+ dcr = di_read(DCR);
+ if (flags & DI_FUNC_FLAG_WRITE_LOCK) {
+ if (flags & DI_FUNC_FLAG_HARD_LOCK)
+ dcr |= DCR_RKHL;
+ else
+ dcr |= DCR_RKSL;
+ }
+ todo_write_val(dcr, DCR);
+ }
+ todo_start();
+
+ if (flags & DI_FUNC_FLAG_ASYNC)
+ return 0;
+
+ rc = todo_wait_done();
+err:
+ di_busy_clear();
+ return rc;
+}
+EXTERN_SYMBOL(dryice_set_random_key);
+
+di_return_t dryice_select_key(di_key_t key, int flags)
+{
+ uint32_t dcr, dksr;
+ di_return_t rc = 0;
+
+ if (di_busy_set())
+ return DI_ERR_BUSY;
+
+ switch (key) {
+ case DI_KEY_FK:
+ dksr = DKSR_IIM_KEY;
+ break;
+ case DI_KEY_PK:
+ dksr = DKSR_PROG_KEY;
+ break;
+ case DI_KEY_RK:
+ dksr = DKSR_RAND_KEY;
+ break;
+ case DI_KEY_FPK:
+ dksr = DKSR_PROG_XOR_IIM_KEY;
+ break;
+ case DI_KEY_FRK:
+ dksr = DKSR_RAND_XOR_IIM_KEY;
+ break;
+ default:
+ rc = DI_ERR_INVAL;
+ goto err;
+ }
+ if (di->key_selected) {
+ rc = DI_ERR_INUSE;
+ goto err;
+ }
+ if (di_state() != DI_STATE_VALID) {
+ rc = DI_ERR_STATE;
+ goto err;
+ }
+ dcr = di_read(DCR);
+ if (dcr & DCR_KSHL) {
+ rc = DI_ERR_HLOCK;
+ goto err;
+ }
+ if (dcr & DCR_KSSL) {
+ rc = DI_ERR_SLOCK;
+ goto err;
+ }
+ todo_init((flags & DI_FUNC_FLAG_ASYNC) != 0);
+
+ /* select key */
+ todo_write_val(dksr, DKSR);
+
+ todo_assign(di->key_selected, 1);
+
+ if (flags & DI_FUNC_LOCK_FLAGS) {
+ dcr = di_read(DCR);
+ if (flags & DI_FUNC_FLAG_WRITE_LOCK) {
+ if (flags & DI_FUNC_FLAG_HARD_LOCK)
+ dcr |= DCR_KSHL;
+ else
+ dcr |= DCR_KSSL;
+ }
+ todo_write_val(dcr, DCR);
+ }
+ todo_start();
+
+ if (flags & DI_FUNC_FLAG_ASYNC)
+ return 0;
+
+ rc = todo_wait_done();
+err:
+ di_busy_clear();
+ return rc;
+}
+EXTERN_SYMBOL(dryice_select_key);
+
+di_return_t dryice_check_key(di_key_t *key)
+{
+ uint32_t dksr;
+ di_return_t rc = 0;
+
+ if (di_busy_set())
+ return DI_ERR_BUSY;
+
+ if (key == NULL) {
+ rc = DI_ERR_INVAL;
+ goto err;
+ }
+
+ dksr = di_read(DKSR);
+
+ if (di_state() != DI_STATE_VALID) {
+ dksr = DKSR_IIM_KEY;
+ rc = DI_ERR_STATE;
+ } else if (dksr == DI_KEY_RK || dksr == DI_KEY_FRK) {
+ if (!(di_read(DSR) & DSR_RKV)) {
+ dksr = DKSR_IIM_KEY;
+ rc = DI_ERR_UNSET;
+ }
+ }
+ switch (dksr) {
+ case DKSR_IIM_KEY:
+ *key = DI_KEY_FK;
+ break;
+ case DKSR_PROG_KEY:
+ *key = DI_KEY_PK;
+ break;
+ case DKSR_RAND_KEY:
+ *key = DI_KEY_RK;
+ break;
+ case DKSR_PROG_XOR_IIM_KEY:
+ *key = DI_KEY_FPK;
+ break;
+ case DKSR_RAND_XOR_IIM_KEY:
+ *key = DI_KEY_FRK;
+ break;
+ }
+err:
+ di_busy_clear();
+ return rc;
+}
+EXTERN_SYMBOL(dryice_check_key);
+
+di_return_t dryice_release_key_selection(void)
+{
+ uint32_t dcr;
+ di_return_t rc = 0;
+
+ if (di_busy_set())
+ return DI_ERR_BUSY;
+
+ if (!di->key_selected) {
+ rc = DI_ERR_UNSET;
+ goto err;
+ }
+ dcr = di_read(DCR);
+ if (dcr & DCR_KSHL) {
+ rc = DI_ERR_HLOCK;
+ goto err;
+ }
+ if (dcr & DCR_KSSL) {
+ rc = DI_ERR_SLOCK;
+ goto err;
+ }
+ di->key_selected = 0;
+
+err:
+ di_busy_clear();
+ return rc;
+}
+EXTERN_SYMBOL(dryice_release_key_selection);
+
+di_return_t dryice_get_tamper_event(uint32_t *events, uint32_t *timestamp,
+ int flags)
+{
+ di_return_t rc = 0;
+
+ if (di_busy_set())
+ return DI_ERR_BUSY;
+
+ if (di_state() == DI_STATE_VALID) {
+ rc = DI_ERR_STATE;
+ goto err;
+ }
+ if (events == NULL) {
+ rc = DI_ERR_INVAL;
+ goto err;
+ }
+ *events = di_read(DSR) & DSR_TAMPER_BITS;
+ if (timestamp) {
+ if (di_state() == DI_STATE_NON_VALID)
+ *timestamp = di_read(DTCMR);
+ else
+ *timestamp = 0;
+ }
+err:
+ di_busy_clear();
+ return rc;
+}
+EXTERN_SYMBOL(dryice_get_tamper_event);
+
+di_return_t dryice_register_callback(void (*func)(di_return_t,
+ unsigned long cookie),
+ unsigned long cookie)
+{
+ di_return_t rc = 0;
+
+ if (di_busy_set())
+ return DI_ERR_BUSY;
+
+ di->cb_func = func;
+ di->cb_cookie = cookie;
+
+ di_busy_clear();
+ return rc;
+}
+EXTERN_SYMBOL(dryice_register_callback);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("DryIce");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/security/dryice.h b/drivers/mxc/security/dryice.h
new file mode 100644
index 000000000000..127e0bcd5d1b
--- /dev/null
+++ b/drivers/mxc/security/dryice.h
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+
+#ifndef __DRYICE_H__
+#define __DRYICE_H__
+
+
+/*!
+ * @file dryice.h
+ * @brief Definition of DryIce API.
+ */
+
+/*! @page dryice_api DryIce API
+ *
+ * Definition of the DryIce API.
+ *
+ * The DryIce API implements a software interface to the DryIce hardware
+ * block. Methods are provided to store, retrieve, generate, and manage
+ * cryptographic keys and to monitor security tamper events.
+ *
+ * See @ref dryice_api for the DryIce API.
+ */
+
+/*!
+ * This defines the SCC key length (in bits)
+ */
+#define SCC_KEY_LEN 168
+
+/*!
+ * This defines the maximum key length (in bits)
+ */
+#define MAX_KEY_LEN 256
+#define MAX_KEY_BYTES ((MAX_KEY_LEN) / 8)
+#define MAX_KEY_WORDS ((MAX_KEY_LEN) / 32)
+
+/*!
+ * @name DryIce Function Flags
+ */
+/*@{*/
+#define DI_FUNC_FLAG_ASYNC 0x01 /*!< do not block */
+#define DI_FUNC_FLAG_READ_LOCK 0x02 /*!< set read lock for this resource */
+#define DI_FUNC_FLAG_WRITE_LOCK 0x04 /*!< set write lock for resource */
+#define DI_FUNC_FLAG_HARD_LOCK 0x08 /*!< locks will be hard (default soft) */
+#define DI_FUNC_FLAG_WORD_KEY 0x10 /*!< key provided as 32-bit words */
+/*@}*/
+
+/*!
+ * @name DryIce Tamper Events
+ */
+/*@{*/
+#define DI_TAMPER_EVENT_WTD (1 << 23) /*!< wire-mesh tampering det */
+#define DI_TAMPER_EVENT_ETBD (1 << 22) /*!< ext tampering det: input B */
+#define DI_TAMPER_EVENT_ETAD (1 << 21) /*!< ext tampering det: input A */
+#define DI_TAMPER_EVENT_EBD (1 << 20) /*!< external boot detected */
+#define DI_TAMPER_EVENT_SAD (1 << 19) /*!< security alarm detected */
+#define DI_TAMPER_EVENT_TTD (1 << 18) /*!< temperature tampering det */
+#define DI_TAMPER_EVENT_CTD (1 << 17) /*!< clock tampering det */
+#define DI_TAMPER_EVENT_VTD (1 << 16) /*!< voltage tampering det */
+#define DI_TAMPER_EVENT_MCO (1 << 3) /*!< monotonic counter overflow */
+#define DI_TAMPER_EVENT_TCO (1 << 2) /*!< time counter overflow */
+/*@}*/
+
+/*!
+ * DryIce Key Sources
+ */
+typedef enum di_key {
+ DI_KEY_FK, /*!< the fused (IIM) key */
+ DI_KEY_PK, /*!< the programmed key */
+ DI_KEY_RK, /*!< the random key */
+ DI_KEY_FPK, /*!< the programmed key XORed with the fused key */
+ DI_KEY_FRK, /*!< the random key XORed with the fused key */
+} di_key_t;
+
+/*!
+ * DryIce Error Codes
+ */
+typedef enum dryice_return {
+ DI_SUCCESS = 0, /*!< operation was successful */
+ DI_ERR_BUSY, /*!< device or resource busy */
+ DI_ERR_STATE, /*!< dryice is in incompatible state */
+ DI_ERR_INUSE, /*!< resource is already in use */
+ DI_ERR_UNSET, /*!< resource has not been initialized */
+ DI_ERR_WRITE, /*!< error occurred during register write */
+ DI_ERR_INVAL, /*!< invalid argument */
+ DI_ERR_FAIL, /*!< operation failed */
+ DI_ERR_HLOCK, /*!< resource is hard locked */
+ DI_ERR_SLOCK, /*!< resource is soft locked */
+ DI_ERR_NOMEM, /*!< out of memory */
+} di_return_t;
+
+/*!
+ * These functions define the DryIce API.
+ */
+
+/*!
+ * Write a given key to the Programmed Key registers in DryIce, and
+ * optionally lock the Programmed Key against either reading or further
+ * writing. The value is held until a call to the release_programmed_key
+ * interface is made, or until the appropriate HW reset if the write-lock
+ * flags are used. Unused key bits will be zeroed.
+ *
+ * @param[in] key_data A pointer to the key data to be programmed, with
+ * the most significant byte or word first. This
+ * will be interpreted as a byte pointer unless the
+ * WORD_KEY flag is set, in which case it will be
+ * treated as a word pointer and the key data will be
+ * read a word at a time, starting with the MSW.
+ * When called asynchronously, the data pointed to by
+ * key_data must persist until the operation completes.
+ *
+ * @param[in] key_bits The number of bits in the key to be stored.
+ * This must be a multiple of 8 and within the
+ * range of 0 and MAX_KEY_LEN.
+ *
+ * @param[in] flags This is a bit-wise OR of the flags to be passed
+ * to the function. Flags can include:
+ * ASYNC, READ_LOCK, WRITE_LOCK, HARD_LOCK, and
+ * WORD_KEY.
+ *
+ * @return Returns SUCCESS (0), BUSY if DryIce is busy, INVAL
+ * on invalid arguments, INUSE if key has already been
+ * programmed, STATE if DryIce is in the wrong state,
+ * HLOCK or SLOCK if the key registers are locked for
+ * writing, and WRITE if a write error occurs
+ * (See #di_return_t).
+ */
+extern di_return_t dryice_set_programmed_key(const void *key_data, int key_bits,
+ int flags);
+
+/*!
+ * Read the Programmed Key registers and write the contents into a buffer.
+ *
+ * @param[out] key_data A byte pointer to where the key data will be written,
+ * with the most significant byte being written first.
+ *
+ * @param[in] key_bits The number of bits of the key to be retrieved.
+ * This must be a multiple of 8 and within the
+ * range of 0 and MAX_KEY_LEN.
+ *
+ * @return Returns SUCCESS (0), BUSY if DryIce is busy, INVAL
+ * on invalid arguments, UNSET if key has not been
+ * programmed, STATE if DryIce is in the wrong state,
+ * and HLOCK or SLOCK if the key registers are locked for
+ * reading (See #di_return_t).
+ */
+extern di_return_t dryice_get_programmed_key(uint8_t *key_data, int key_bits);
+
+/*!
+ * Allow the set_programmed_key interface to be used to write a new
+ * Programmed Key to DryIce. Note that this interface does not overwrite
+ * the value in the Programmed Key registers.
+ *
+ * @return Returns SUCCESS (0), BUSY if DryIce is busy,
+ * UNSET if the key has not been previously set, and
+ * HLOCK or SLOCK if the key registers are locked for
+ * writing (See #di_return_t).
+ */
+extern di_return_t dryice_release_programmed_key(void);
+
+/*!
+ * Generate and load a new Random Key in DryIce, and optionally lock the
+ * Random Key against further change.
+ *
+ * @param[in] flags This is a bit-wise OR of the flags to be passed
+ * to the function. Flags can include:
+ * ASYNC, READ_LOCK, WRITE_LOCK, and HARD_LOCK.
+ *
+ * @return Returns SUCCESS (0), BUSY if DryIce is busy, STATE
+ * if DryIce is in the wrong state, FAIL if the key gen
+ * failed, HLOCK or SLOCK if the key registers are
+ * locked, and WRITE if a write error occurs
+ * (See #di_return_t).
+ */
+extern di_return_t dryice_set_random_key(int flags);
+
+/*!
+ * Set the key selection in DryIce to determine the key used by an
+ * encryption module such as SCC. The selection is held until a call to the
+ * Release Selected Key interface is made, or until the appropriate HW
+ * reset if the LOCK flags are used.
+ *
+ * @param[in] key The source of the key to be used by the SCC
+ * (See #di_key_t).
+ *
+ * @param[in] flags This is a bit-wise OR of the flags to be passed
+ * to the function. Flags can include:
+ * ASYNC, WRITE_LOCK, and HARD_LOCK.
+ *
+ * @return Returns SUCCESS (0), BUSY if DryIce is busy, INVAL
+ * on invalid arguments, INUSE if a selection has already
+ * been made, STATE if DryIce is in the wrong state,
+ * HLOCK or SLOCK if the selection register is locked,
+ * and WRITE if a write error occurs
+ */
+extern di_return_t dryice_select_key(di_key_t key, int flags);
+
+/*!
+ * Check which key will be used in the SCC. This is needed because in some
+ * DryIce states, the Key Select Register is overridden by a default value
+ * (the Fused/IIM key).
+ *
+ * @param[out] key The source of the key that is currently selected for
+ * use by the SCC. This may be different from the key
+ * specified by the dryice_select_key function
+ * (See #di_key_t). This value is set even if an error
+ * code (except for BUSY) is returned.
+ *
+ * @return Returns SUCCESS (0), BUSY if DryIce is busy, STATE if
+ * DryIce is in the wrong state, INVAL on invalid
+ * arguments, or UNSET if no key has been selected
+ * (See #di_return_t).
+ */
+extern di_return_t dryice_check_key(di_key_t *key);
+
+/*!
+ * Allow the dryice_select_key interface to be used to set a new key selection
+ * in DryIce. Note that this interface does not overwrite the value in DryIce.
+ *
+ * @return Returns SUCCESS (0), BUSY if DryIce is busy, UNSET
+ * if the no selection has been made previously, and
+ * HLOCK or SLOCK if the selection register is locked
+ * (See #di_return_t).
+ */
+extern di_return_t dryice_release_key_selection(void);
+
+/*!
+ * Returns tamper-detection status bits. Also an optional timestamp when
+ * DryIce is in the Non-valid state. If DryIce is not in Failure or Non-valid
+ * state, this interface returns a failure code.
+ *
+ * @param[out] events This is a bit-wise OR of the following events:
+ * WTD (Wire Mesh), ETBD (External Tamper B),
+ * ETAD (External Tamper A), EBD (External Boot),
+ * SAD (Security Alarm), TTD (Temperature Tamper),
+ * CTD (Clock Tamper), VTD (Voltage Tamper),
+ * MCO (Monolithic Counter Overflow), and
+ * TCO (Time Counter Overflow).
+ *
+ * @param[out] timestamp This is the value of the time counter in seconds
+ * when the tamper occurred. A timestamp will not be
+ * returned if a NULL pointer is specified. If DryIce
+ * is not in the Non-valid state the time cannot be
+ * read, so a timestamp of 0 will be returned.
+ *
+ * @param[in] flags This is a bit-wise OR of the flags to be passed
+ * to the function. Flags is ignored currently by
+ * this function.
+ *
+ * @return Returns SUCCESS (0), BUSY if DryIce is busy, and
+ * INVAL on invalid arguments (See #di_return_t).
+ */
+extern di_return_t
+dryice_get_tamper_event(uint32_t *events, uint32_t *timestamp, int flags);
+
+/*!
+ * Provide a callback function to be called upon the completion of DryIce calls
+ * that are executed asynchronously.
+ *
+ * @param[in] func This is a pointer to a function of type:
+ * void callback(di_return_t rc, unsigned long cookie)
+ * The return code of the async function is passed
+ * back in "rc" along with the cookie provided when
+ * registering the callback.
+ *
+ * @param[in] cookie This is an "opaque" cookie of type unsigned long that
+ * is returned on subsequent callbacks. It may be of any
+ * value.
+ *
+ * @return Returns SUCCESS (0), or BUSY if DryIce is busy
+ * (See #di_return_t).
+ */
+extern di_return_t dryice_register_callback(void (*func)(di_return_t rc,
+ unsigned long cookie),
+ unsigned long cookie);
+
+#endif /* __DRYICE_H__ */
diff --git a/drivers/mxc/security/mxc_scc.c b/drivers/mxc/security/mxc_scc.c
new file mode 100644
index 000000000000..20d54bfb5362
--- /dev/null
+++ b/drivers/mxc/security/mxc_scc.c
@@ -0,0 +1,2386 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mxc_scc.c
+ *
+ * This is the driver code for the Security Controller (SCC). It has no device
+ * driver interface, so no user programs may access it. Its interaction with
+ * the Linux kernel is from calls to #scc_init() when the driver is loaded, and
+ * #scc_cleanup() should the driver be unloaded. The driver uses locking and
+ * (task-sleep/task-wakeup) functions of the kernel. It also registers itself
+ * to handle the interrupt line(s) from the SCC.
+ *
+ * Other drivers in the kernel may use the remaining API functions to get at
+ * the services of the SCC. The main service provided is the Secure Memory,
+ * which allows encoding and decoding of secrets with a per-chip secret key.
+ *
+ * The SCC is single-threaded, and so is this module. When the scc_crypt()
+ * routine is called, it will lock out other accesses to the function. If
+ * another task is already in the module, the subsequent caller will spin on a
+ * lock waiting for the other access to finish.
+ *
+ * Note that long crypto operations could cause a task to spin for a while,
+ * preventing other kernel work (other than interrupt processing) to get done.
+ *
+ * The external (kernel module) interface is through the following functions:
+ * @li scc_get_configuration()
+ * @li scc_crypt()
+ * @li scc_zeroize_memories()
+ * @li scc_monitor_security_failure()
+ * @li scc_stop_monitoring_security_failure()
+ * @li scc_set_sw_alarm()
+ * @li scc_read_register()
+ * @li scc_write_register()
+ *
+ * All other functions are internal to the driver.
+ *
+ * @ingroup MXCSCC
+*/
+#include "sahara2/include/fsl_platform.h"
+#include "sahara2/include/portable_os.h"
+#include "mxc_scc_internals.h"
+
+#include <linux/delay.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
+
+#include <linux/device.h>
+#include <mach/clock.h>
+#include <linux/device.h>
+
+#else
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#endif
+
+/*!
+ * This is the set of errors which signal that access to the SCM RAM has
+ * failed or will fail.
+ */
+#define SCM_ACCESS_ERRORS \
+ (SCM_ERR_USER_ACCESS | SCM_ERR_ILLEGAL_ADDRESS | \
+ SCM_ERR_ILLEGAL_MASTER | SCM_ERR_CACHEABLE_ACCESS | \
+ SCM_ERR_UNALIGNED_ACCESS | SCM_ERR_BYTE_ACCESS | \
+ SCM_ERR_INTERNAL_ERROR | SCM_ERR_SMN_BLOCKING_ACCESS | \
+ SCM_ERR_CIPHERING | SCM_ERR_ZEROIZING | SCM_ERR_BUSY)
+/******************************************************************************
+ *
+ * Global / Static Variables
+ *
+ *****************************************************************************/
+
+/*!
+ * This is type void* so that a) it cannot directly be dereferenced,
+ * and b) pointer arithmetic on it will function in a 'normal way' for
+ * the offsets in scc_defines.h
+ *
+ * scc_base is the location in the iomap where the SCC's registers
+ * (and memory) start.
+ *
+ * The referenced data is declared volatile so that the compiler will
+ * not make any assumptions about the value of registers in the SCC,
+ * and thus will always reload the register into CPU memory before
+ * using it (i.e. wherever it is referenced in the driver).
+ *
+ * This value should only be referenced by the #SCC_READ_REGISTER and
+ * #SCC_WRITE_REGISTER macros and their ilk. All dereferences must be
+ * 32 bits wide.
+ */
+static volatile void *scc_base;
+
+/*! Array to hold function pointers registered by
+ #scc_monitor_security_failure() and processed by
+ #scc_perform_callbacks() */
+static void (*scc_callbacks[SCC_CALLBACK_SIZE]) (void);
+
+/*! Structure returned by #scc_get_configuration() */
+static scc_config_t scc_configuration = {
+ .driver_major_version = SCC_DRIVER_MAJOR_VERSION_1,
+ .driver_minor_version = SCC_DRIVER_MINOR_VERSION_8,
+ .scm_version = -1,
+ .smn_version = -1,
+ .block_size_bytes = -1,
+ .black_ram_size_blocks = -1,
+ .red_ram_size_blocks = -1
+};
+
+/*! Key Control Information. Integrity is controlled by use of
+ #scc_crypto_lock. */
+static struct scc_key_slot scc_key_info[SCC_KEY_SLOTS];
+
+/*! Internal flag to know whether SCC is in Failed state (and thus many
+ * registers are unavailable). Once it goes failed, it never leaves it. */
+static volatile enum scc_status scc_availability = SCC_STATUS_INITIAL;
+
+/*! Flag to say whether interrupt handler has been registered for
+ * SMN interrupt */
+static int smn_irq_set = 0;
+
+/*! Flag to say whether interrupt handler has been registered for
+ * SCM interrupt */
+static int scm_irq_set = 0;
+
+/*! This lock protects the #scc_callbacks list as well as the @c
+ * callbacks_performed flag in #scc_perform_callbacks. Since the data this
+ * protects may be read or written from either interrupt or base level, all
+ * operations should use the irqsave/irqrestore or similar to make sure that
+ * interrupts are inhibited when locking from base level.
+ */
+static spinlock_t scc_callbacks_lock = SPIN_LOCK_UNLOCKED;
+
+/*!
+ * Ownership of this lock prevents conflicts on the crypto operation in the SCC
+ * and the integrity of the #scc_key_info.
+ */
+static spinlock_t scc_crypto_lock = SPIN_LOCK_UNLOCKED;
+
+/*! Calculated once for quick reference to size of the unreserved space in one
+ * RAM in SCM.
+ */
+static uint32_t scc_memory_size_bytes;
+
+/*! Calculated once for quick reference to size of SCM address space */
+static uint32_t scm_highest_memory_address;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18))
+#ifndef SCC_CLOCK_NOT_GATED
+/*! Pointer to SCC's clock information. Initialized during scc_init(). */
+static struct clk *scc_clk = NULL;
+#endif
+#endif
+
+/*! The lookup table for an 8-bit value. Calculated once
+ * by #scc_init_ccitt_crc().
+ */
+static uint16_t scc_crc_lookup_table[256];
+
+/*! Fixed padding for appending to plaintext to fill out a block */
+static uint8_t scc_block_padding[8] =
+ { SCC_DRIVER_PAD_CHAR, 0, 0, 0, 0, 0, 0, 0 };
+
+/******************************************************************************
+ *
+ * Function Implementations - Externally Accessible
+ *
+ *****************************************************************************/
+
+/*****************************************************************************/
+/* fn scc_init() */
+/*****************************************************************************/
+/*!
+ * Initialize the driver at boot time or module load time.
+ *
+ * Register with the kernel as the interrupt handler for the SCC interrupt
+ * line(s).
+ *
+ * Map the SCC's register space into the driver's memory space.
+ *
+ * Query the SCC for its configuration and status. Save the configuration in
+ * #scc_configuration and save the status in #scc_availability. Called by the
+ * kernel.
+ *
+ * Do any locking/wait queue initialization which may be necessary.
+ *
+ * The availability fuse may be checked, depending on platform.
+ */
+static int scc_init(void)
+{
+ uint32_t smn_status;
+ int i;
+ int return_value = -EIO; /* assume error */
+ if (scc_availability == SCC_STATUS_INITIAL) {
+
+ /* Set this until we get an initial reading */
+ scc_availability = SCC_STATUS_CHECKING;
+
+ /* Initialize the constant for the CRC function */
+ scc_init_ccitt_crc();
+
+ /* initialize the callback table */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ scc_callbacks[i] = 0;
+ }
+
+ /* Initialize key slots */
+ for (i = 0; i < SCC_KEY_SLOTS; i++) {
+ scc_key_info[i].offset = i * SCC_KEY_SLOT_SIZE;
+ scc_key_info[i].status = 0; /* unassigned */
+ }
+
+ /* Enable the SCC clock on platforms where it is gated */
+#ifndef SCC_CLOCK_NOT_GATED
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
+ mxc_clks_enable(SCC_CLK);
+#else
+
+ scc_clk = clk_get(NULL, "scc_clk");
+ if (scc_clk != ERR_PTR(ENOENT)) {
+ clk_enable(scc_clk);
+ }
+#endif /* LINUX_VERSION_CODE */
+
+#endif /* SCC_CLOCK_NOT_GATED */
+ /* See whether there is an SCC available */
+ if (0 && !SCC_ENABLED()) {
+ os_printk(KERN_ERR
+ "SCC: Fuse for SCC is set to disabled. Exiting.\n");
+ } else {
+ /* Map the SCC (SCM and SMN) memory on the internal bus into
+ kernel address space */
+ scc_base = (void *)IO_ADDRESS(SCC_BASE);
+
+ /* If that worked, we can try to use the SCC */
+ if (scc_base == NULL) {
+ os_printk(KERN_ERR
+ "SCC: Register mapping failed. Exiting.\n");
+ } else {
+ /* Get SCM into 'clean' condition w/interrupts cleared &
+ disabled */
+ SCC_WRITE_REGISTER(SCM_INTERRUPT_CTRL,
+ SCM_INTERRUPT_CTRL_CLEAR_INTERRUPT
+ |
+ SCM_INTERRUPT_CTRL_MASK_INTERRUPTS);
+
+ /* Clear error status register (any write will do it) */
+ SCC_WRITE_REGISTER(SCM_ERROR_STATUS, 0);
+
+ /*
+ * There is an SCC. Determine its current state. Side effect
+ * is to populate scc_config and scc_availability
+ */
+ smn_status = scc_grab_config_values();
+
+ /* Try to set up interrupt handler(s) */
+ if (scc_availability == SCC_STATUS_OK) {
+ if (setup_interrupt_handling() != 0) {
+ unsigned condition;
+ /*!
+ * The error could be only that the SCM interrupt was
+ * not set up. This interrupt is always masked, so
+ * that is not an issue.
+ *
+ * The SMN's interrupt may be shared on that line, it
+ * may be separate, or it may not be wired. Do what
+ * is necessary to check its status.
+ *
+ * Although the driver is coded for possibility of not
+ * having SMN interrupt, the fact that there is one
+ * means it should be available and used.
+ */
+#ifdef USE_SMN_INTERRUPT
+ condition = !smn_irq_set; /* Separate. Check SMN binding */
+#elif !defined(NO_SMN_INTERRUPT)
+ condition = !scm_irq_set; /* Shared. Check SCM binding */
+#else
+ condition = FALSE; /* SMN not wired at all. Ignore. */
+#endif
+ /* setup was not able to set up SMN interrupt */
+ scc_availability =
+ SCC_STATUS_UNIMPLEMENTED;
+ } /* interrupt handling returned non-zero */
+ } /* availability is OK */
+ if (scc_availability == SCC_STATUS_OK) {
+ /* Get SMN into 'clean' condition w/interrupts cleared &
+ enabled */
+ SCC_WRITE_REGISTER(SMN_COMMAND,
+ SMN_COMMAND_CLEAR_INTERRUPT
+ |
+ SMN_COMMAND_ENABLE_INTERRUPT);
+ }
+ /* availability is still OK */
+ } /* if scc_base != NULL */
+
+ } /* if SCC_ENABLED() */
+
+ /*
+ * If status is SCC_STATUS_UNIMPLEMENTED or is still
+ * SCC_STATUS_CHECKING, could be leaving here with the driver partially
+ * initialized. In either case, cleanup (which will mark the SCC as
+ * UNIMPLEMENTED).
+ */
+ if (scc_availability == SCC_STATUS_CHECKING ||
+ scc_availability == SCC_STATUS_UNIMPLEMENTED) {
+ scc_cleanup();
+ } else {
+ return_value = 0; /* All is well */
+ }
+ }
+ /* ! STATUS_INITIAL */
+ pr_debug("SCC: Driver Status is %s\n",
+ (scc_availability == SCC_STATUS_INITIAL) ? "INITIAL" :
+ (scc_availability == SCC_STATUS_CHECKING) ? "CHECKING" :
+ (scc_availability ==
+ SCC_STATUS_UNIMPLEMENTED) ? "UNIMPLEMENTED"
+ : (scc_availability ==
+ SCC_STATUS_OK) ? "OK" : (scc_availability ==
+ SCC_STATUS_FAILED) ? "FAILED" :
+ "UNKNOWN");
+
+ return return_value;
+} /* scc_init */
+
+/*****************************************************************************/
+/* fn scc_cleanup() */
+/*****************************************************************************/
+/*!
+ * Perform cleanup before driver/module is unloaded by setting the machine
+ * state close to what it was when the driver was loaded. This function is
+ * called when the kernel is shutting down or when this driver is being
+ * unloaded.
+ *
+ * A driver like this should probably never be unloaded, especially if there
+ * are other module relying upon the callback feature for monitoring the SCC
+ * status.
+ *
+ * In any case, cleanup the callback table (by clearing out all of the
+ * pointers). Deregister the interrupt handler(s). Unmap SCC registers.
+ *
+ */
+static void scc_cleanup(void)
+{
+ int i;
+
+ /* Mark the driver / SCC as unusable. */
+ scc_availability = SCC_STATUS_UNIMPLEMENTED;
+
+ /* Clear out callback table */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ scc_callbacks[i] = 0;
+ }
+
+ /* If SCC has been mapped in, clean it up and unmap it */
+ if (scc_base) {
+ /* For the SCM, disable interrupts, zeroize RAMs. Interrupt
+ * status will appear because zeroize will complete. */
+ SCC_WRITE_REGISTER(SCM_INTERRUPT_CTRL,
+ SCM_INTERRUPT_CTRL_MASK_INTERRUPTS |
+ SCM_INTERRUPT_CTRL_ZEROIZE_MEMORY);
+
+ /* For the SMN, clear and disable interrupts */
+ SCC_WRITE_REGISTER(SMN_COMMAND, SMN_COMMAND_CLEAR_INTERRUPT);
+
+ /* remove virtual mapping */
+ iounmap((void *)scc_base);
+ }
+
+ /* Now that interrupts cannot occur, disassociate driver from the interrupt
+ * lines.
+ */
+
+ /* Deregister SCM interrupt handler */
+ if (scm_irq_set) {
+ os_deregister_interrupt(INT_SCC_SCM);
+ }
+
+ /* Deregister SMN interrupt handler */
+ if (smn_irq_set) {
+#ifdef USE_SMN_INTERRUPT
+ os_deregister_interrupt(INT_SCC_SMN);
+#endif
+ }
+ pr_debug("SCC driver cleaned up.\n");
+
+} /* scc_cleanup */
+
+/*****************************************************************************/
+/* fn scc_get_configuration() */
+/*****************************************************************************/
+scc_config_t *scc_get_configuration(void)
+{
+ /*
+ * If some other driver calls scc before the kernel does, make sure that
+ * this driver's initialization is performed.
+ */
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /*!
+ * If there is no SCC, yet the driver exists, the value -1 will be in
+ * the #scc_config_t fields for other than the driver versions.
+ */
+ return &scc_configuration;
+} /* scc_get_configuration */
+
+/*****************************************************************************/
+/* fn scc_zeroize_memories() */
+/*****************************************************************************/
+scc_return_t scc_zeroize_memories(void)
+{
+ scc_return_t return_status = SCC_RET_FAIL;
+ uint32_t status;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ if (scc_availability == SCC_STATUS_OK) {
+ unsigned long irq_flags; /* for IRQ save/restore */
+
+ /* Lock access to crypto memory of the SCC */
+ spin_lock_irqsave(&scc_crypto_lock, irq_flags);
+
+ /* Start the Zeroize by setting a bit in the SCM_INTERRUPT_CTRL
+ * register */
+ SCC_WRITE_REGISTER(SCM_INTERRUPT_CTRL,
+ SCM_INTERRUPT_CTRL_MASK_INTERRUPTS
+ | SCM_INTERRUPT_CTRL_ZEROIZE_MEMORY);
+
+ scc_wait_completion();
+
+ /* Get any error info */
+ status = SCC_READ_REGISTER(SCM_ERROR_STATUS);
+
+ /* unlock the SCC */
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+
+ if (!(status & SCM_ERR_ZEROIZE_FAILED)) {
+ return_status = SCC_RET_OK;
+ } else {
+ pr_debug
+ ("SCC: Zeroize failed. SCM Error Status is 0x%08x\n",
+ status);
+ }
+
+ /* Clear out any status. */
+ SCC_WRITE_REGISTER(SCM_INTERRUPT_CTRL,
+ SCM_INTERRUPT_CTRL_CLEAR_INTERRUPT
+ | SCM_INTERRUPT_CTRL_MASK_INTERRUPTS);
+
+ /* and any error status */
+ SCC_WRITE_REGISTER(SCM_ERROR_STATUS, 0);
+ }
+
+ return return_status;
+} /* scc_zeroize_memories */
+
+/*****************************************************************************/
+/* fn scc_crypt() */
+/*****************************************************************************/
+scc_return_t
+scc_crypt(unsigned long count_in_bytes, const uint8_t * data_in,
+ const uint8_t * init_vector,
+ scc_enc_dec_t direction, scc_crypto_mode_t crypto_mode,
+ scc_verify_t check_mode, uint8_t * data_out,
+ unsigned long *count_out_bytes)
+
+{
+ scc_return_t return_code = SCC_RET_FAIL;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ (void)scc_update_state(); /* in case no interrupt line from SMN */
+
+ /* make initial error checks */
+ if (scc_availability != SCC_STATUS_OK
+ || count_in_bytes == 0
+ || data_in == 0
+ || data_out == 0
+ || (crypto_mode != SCC_CBC_MODE && crypto_mode != SCC_ECB_MODE)
+ || (crypto_mode == SCC_CBC_MODE && init_vector == NULL)
+ || (direction != SCC_ENCRYPT && direction != SCC_DECRYPT)
+ || (check_mode == SCC_VERIFY_MODE_NONE &&
+ count_in_bytes % SCC_BLOCK_SIZE_BYTES() != 0)
+ || (direction == SCC_DECRYPT &&
+ count_in_bytes % SCC_BLOCK_SIZE_BYTES() != 0)
+ || (check_mode != SCC_VERIFY_MODE_NONE &&
+ check_mode != SCC_VERIFY_MODE_CCITT_CRC)) {
+ pr_debug
+ ("SCC: scc_crypt() count_in_bytes_ok = %d; data_in_ok = %d;"
+ " data_out_ok = %d; iv_ok = %d\n", !(count_in_bytes == 0),
+ !(data_in == 0), !(data_out == 0),
+ !(crypto_mode == SCC_CBC_MODE && init_vector == NULL));
+ pr_debug("SCC: scc_crypt() mode_ok=%d; direction_ok=%d;"
+ " size_ok=%d, check_mode_ok=%d\n",
+ !(crypto_mode != SCC_CBC_MODE
+ && crypto_mode != SCC_ECB_MODE),
+ !(direction != SCC_ENCRYPT
+ && direction != SCC_DECRYPT),
+ !((check_mode == SCC_VERIFY_MODE_NONE
+ && count_in_bytes % SCC_BLOCK_SIZE_BYTES() != 0)
+ || (direction == SCC_DECRYPT
+ && count_in_bytes % SCC_BLOCK_SIZE_BYTES() !=
+ 0)), !(check_mode != SCC_VERIFY_MODE_NONE
+ && check_mode !=
+ SCC_VERIFY_MODE_CCITT_CRC));
+ pr_debug("SCC: scc_crypt() detected bad argument\n");
+ } else {
+ /* Start settings for write to SCM_CONTROL register */
+ uint32_t scc_control = SCM_CONTROL_START_CIPHER;
+ unsigned long irq_flags; /* for IRQ save/restore */
+
+ /* Lock access to crypto memory of the SCC */
+ spin_lock_irqsave(&scc_crypto_lock, irq_flags);
+
+ /* Special needs for CBC Mode */
+ if (crypto_mode == SCC_CBC_MODE) {
+ scc_control |= SCM_CBC_MODE; /* change default of ECB */
+ /* Put in Initial Context. Vector registers are contiguous */
+ copy_to_scc(init_vector, SCM_INIT_VECTOR_0,
+ SCC_BLOCK_SIZE_BYTES(), NULL);
+ }
+
+ /* Fill the RED_START register */
+ SCC_WRITE_REGISTER(SCM_RED_START,
+ SCM_NON_RESERVED_OFFSET /
+ SCC_BLOCK_SIZE_BYTES());
+
+ /* Fill the BLACK_START register */
+ SCC_WRITE_REGISTER(SCM_BLACK_START,
+ SCM_NON_RESERVED_OFFSET /
+ SCC_BLOCK_SIZE_BYTES());
+
+ if (direction == SCC_ENCRYPT) {
+ /* Check for sufficient space in data_out */
+ if (check_mode == SCC_VERIFY_MODE_NONE) {
+ if (*count_out_bytes < count_in_bytes) {
+ return_code =
+ SCC_RET_INSUFFICIENT_SPACE;
+ }
+ } else { /* SCC_VERIFY_MODE_CCITT_CRC */
+ /* Calculate extra bytes needed for crc (2) and block
+ padding */
+ int padding_needed =
+ CRC_SIZE_BYTES + SCC_BLOCK_SIZE_BYTES() -
+ ((count_in_bytes + CRC_SIZE_BYTES)
+ % SCC_BLOCK_SIZE_BYTES());
+
+ /* Verify space is available */
+ if (*count_out_bytes <
+ count_in_bytes + padding_needed) {
+ return_code =
+ SCC_RET_INSUFFICIENT_SPACE;
+ }
+ }
+ /* If did not detect space error, do the encryption */
+ if (return_code != SCC_RET_INSUFFICIENT_SPACE) {
+ return_code =
+ scc_encrypt(count_in_bytes, data_in,
+ scc_control, data_out,
+ check_mode ==
+ SCC_VERIFY_MODE_CCITT_CRC,
+ count_out_bytes);
+ }
+
+ }
+ /* direction == SCC_ENCRYPT */
+ else { /* SCC_DECRYPT */
+ /* Check for sufficient space in data_out */
+ if (check_mode == SCC_VERIFY_MODE_NONE) {
+ if (*count_out_bytes < count_in_bytes) {
+ return_code =
+ SCC_RET_INSUFFICIENT_SPACE;
+ }
+ } else { /* SCC_VERIFY_MODE_CCITT_CRC */
+ /* Do initial check. Assume last block (of padding) and CRC
+ * will get stripped. After decipher is done and padding is
+ * removed, will know exact value.
+ */
+ int possible_size =
+ (int)count_in_bytes - CRC_SIZE_BYTES -
+ SCC_BLOCK_SIZE_BYTES();
+ if ((int)*count_out_bytes < possible_size) {
+ pr_debug
+ ("SCC: insufficient decrypt space %ld/%d.\n",
+ *count_out_bytes, possible_size);
+ return_code =
+ SCC_RET_INSUFFICIENT_SPACE;
+ }
+ }
+
+ /* If did not detect space error, do the decryption */
+ if (return_code != SCC_RET_INSUFFICIENT_SPACE) {
+ return_code =
+ scc_decrypt(count_in_bytes, data_in,
+ scc_control, data_out,
+ check_mode ==
+ SCC_VERIFY_MODE_CCITT_CRC,
+ count_out_bytes);
+ }
+
+ } /* SCC_DECRYPT */
+
+ /* unlock the SCC */
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+
+ } /* else no initial error */
+
+ return return_code;
+} /* scc_crypt */
+
+/*****************************************************************************/
+/* fn scc_set_sw_alarm() */
+/*****************************************************************************/
+void scc_set_sw_alarm(void)
+{
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* Update scc_availability based on current SMN status. This might
+ * perform callbacks.
+ */
+ (void)scc_update_state();
+
+ /* if everything is OK, make it fail */
+ if (scc_availability == SCC_STATUS_OK) {
+
+ /* sound the alarm (and disable SMN interrupts */
+ SCC_WRITE_REGISTER(SMN_COMMAND, SMN_COMMAND_SET_SOFTWARE_ALARM);
+
+ scc_availability = SCC_STATUS_FAILED; /* Remember what we've done */
+
+ /* In case SMN interrupt is not available, tell the world */
+ scc_perform_callbacks();
+ }
+
+ return;
+} /* scc_set_sw_alarm */
+
+/*****************************************************************************/
+/* fn scc_monitor_security_failure() */
+/*****************************************************************************/
+scc_return_t scc_monitor_security_failure(void callback_func(void))
+{
+ int i;
+ unsigned long irq_flags; /* for IRQ save/restore */
+ scc_return_t return_status = SCC_RET_TOO_MANY_FUNCTIONS;
+ int function_stored = FALSE;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* Acquire lock of callbacks table. Could be spin_lock_irq() if this
+ * routine were just called from base (not interrupt) level
+ */
+ spin_lock_irqsave(&scc_callbacks_lock, irq_flags);
+
+ /* Search through table looking for empty slot */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ if (scc_callbacks[i] == callback_func) {
+ if (function_stored) {
+ /* Saved duplicate earlier. Clear this later one. */
+ scc_callbacks[i] = NULL;
+ }
+ /* Exactly one copy is now stored */
+ return_status = SCC_RET_OK;
+ break;
+ } else if (scc_callbacks[i] == NULL && !function_stored) {
+ /* Found open slot. Save it and remember */
+ scc_callbacks[i] = callback_func;
+ return_status = SCC_RET_OK;
+ function_stored = TRUE;
+ }
+ }
+
+ /* Free the lock */
+ spin_unlock_irqrestore(&scc_callbacks_lock, irq_flags);
+
+ return return_status;
+} /* scc_monitor_security_failure */
+
+/*****************************************************************************/
+/* fn scc_stop_monitoring_security_failure() */
+/*****************************************************************************/
+void scc_stop_monitoring_security_failure(void callback_func(void))
+{
+ unsigned long irq_flags; /* for IRQ save/restore */
+ int i;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* Acquire lock of callbacks table. Could be spin_lock_irq() if this
+ * routine were just called from base (not interrupt) level
+ */
+ spin_lock_irqsave(&scc_callbacks_lock, irq_flags);
+
+ /* Search every entry of the table for this function */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ if (scc_callbacks[i] == callback_func) {
+ scc_callbacks[i] = NULL; /* found instance - clear it out */
+ break;
+ }
+ }
+
+ /* Free the lock */
+ spin_unlock_irqrestore(&scc_callbacks_lock, irq_flags);
+
+ return;
+} /* scc_stop_monitoring_security_failure */
+
+/*****************************************************************************/
+/* fn scc_read_register() */
+/*****************************************************************************/
+scc_return_t scc_read_register(int register_offset, uint32_t * value)
+{
+ scc_return_t return_status = SCC_RET_FAIL;
+ uint32_t smn_status;
+ uint32_t scm_status;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* First layer of protection -- completely unaccessible SCC */
+ if (scc_availability != SCC_STATUS_UNIMPLEMENTED) {
+
+ /* Second layer -- that offset is valid */
+ if (register_offset != SMN_BITBANK_DECREMENT && /* write only! */
+ check_register_offset(register_offset) == SCC_RET_OK) {
+
+ /* Get current status / update local state */
+ smn_status = scc_update_state();
+ scm_status = SCC_READ_REGISTER(SCM_STATUS);
+
+ /*
+ * Third layer - verify that the register being requested is
+ * available in the current state of the SCC.
+ */
+ if ((return_status =
+ check_register_accessible(register_offset,
+ smn_status,
+ scm_status)) ==
+ SCC_RET_OK) {
+ *value = SCC_READ_REGISTER(register_offset);
+ }
+ }
+ }
+
+ return return_status;
+} /* scc_read_register */
+
+/*****************************************************************************/
+/* fn scc_write_register() */
+/*****************************************************************************/
+scc_return_t scc_write_register(int register_offset, uint32_t value)
+{
+ scc_return_t return_status = SCC_RET_FAIL;
+ uint32_t smn_status;
+ uint32_t scm_status;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* First layer of protection -- completely unaccessible SCC */
+ if (scc_availability != SCC_STATUS_UNIMPLEMENTED) {
+
+ /* Second layer -- that offset is valid */
+ if (!(register_offset == SCM_STATUS || /* These registers are */
+ register_offset == SCM_CONFIGURATION || /* Read Only */
+ register_offset == SMN_BIT_COUNT ||
+ register_offset == SMN_TIMER) &&
+ check_register_offset(register_offset) == SCC_RET_OK) {
+
+ /* Get current status / update local state */
+ smn_status = scc_update_state();
+ scm_status = SCC_READ_REGISTER(SCM_STATUS);
+
+ /*
+ * Third layer - verify that the register being requested is
+ * available in the current state of the SCC.
+ */
+ if (check_register_accessible
+ (register_offset, smn_status, scm_status) == 0) {
+ SCC_WRITE_REGISTER(register_offset, value);
+ return_status = SCC_RET_OK;
+ }
+ }
+ }
+
+ return return_status;
+} /* scc_write_register() */
+
+/******************************************************************************
+ *
+ * Function Implementations - Internal
+ *
+ *****************************************************************************/
+
+/*****************************************************************************/
+/* fn scc_irq() */
+/*****************************************************************************/
+/*!
+ * This is the interrupt handler for the SCC.
+ *
+ * This function checks the SMN Status register to see whether it
+ * generated the interrupt, then it checks the SCM Status register to
+ * see whether it needs attention.
+ *
+ * If an SMN Interrupt is active, then the SCC state set to failure, and
+ * #scc_perform_callbacks() is invoked to notify any interested parties.
+ *
+ * The SCM Interrupt should be masked, as this driver uses polling to determine
+ * when the SCM has completed a crypto or zeroing operation. Therefore, if the
+ * interrupt is active, the driver will just clear the interrupt and (re)mask.
+ *
+ */
+OS_DEV_ISR(scc_irq)
+{
+ uint32_t smn_status;
+ uint32_t scm_status;
+ int handled = 0; /* assume interrupt isn't from SMN */
+#if defined(USE_SMN_INTERRUPT)
+ int smn_irq = INT_SCC_SMN; /* SMN interrupt is on a line by itself */
+#elif defined (NO_SMN_INTERRUPT)
+ int smn_irq = -1; /* not wired to CPU at all */
+#else
+ int smn_irq = INT_SCC_SCM; /* SMN interrupt shares a line with SCM */
+#endif
+
+ /* Update current state... This will perform callbacks... */
+ smn_status = scc_update_state();
+
+ /* SMN is on its own interrupt line. Verify the IRQ was triggered
+ * before clearing the interrupt and marking it handled. */
+ if ((os_dev_get_irq() == smn_irq) &&
+ (smn_status & SMN_STATUS_SMN_STATUS_IRQ)) {
+ SCC_WRITE_REGISTER(SMN_COMMAND, SMN_COMMAND_CLEAR_INTERRUPT);
+ handled++; /* tell kernel that interrupt was handled */
+ }
+
+ /* Check on the health of the SCM */
+ scm_status = SCC_READ_REGISTER(SCM_STATUS);
+
+ /* The driver masks interrupts, so this should never happen. */
+ if (os_dev_get_irq() == INT_SCC_SCM) {
+ /* but if it does, try to prevent it in the future */
+ SCC_WRITE_REGISTER(SCM_INTERRUPT_CTRL,
+ SCM_INTERRUPT_CTRL_CLEAR_INTERRUPT
+ | SCM_INTERRUPT_CTRL_MASK_INTERRUPTS);
+ handled++;
+ }
+
+ /* Any non-zero value of handled lets kernel know we got something */
+ return IRQ_RETVAL(handled);
+}
+
+/*****************************************************************************/
+/* fn scc_perform_callbacks() */
+/*****************************************************************************/
+/*! Perform callbacks registered by #scc_monitor_security_failure().
+ *
+ * Make sure callbacks only happen once... Since there may be some reason why
+ * the interrupt isn't generated, this routine could be called from base(task)
+ * level.
+ *
+ * One at a time, go through #scc_callbacks[] and call any non-null pointers.
+ */
+static void scc_perform_callbacks(void)
+{
+ static int callbacks_performed = 0;
+ unsigned long irq_flags; /* for IRQ save/restore */
+ int i;
+
+ /* Acquire lock of callbacks table and callbacks_performed flag */
+ spin_lock_irqsave(&scc_callbacks_lock, irq_flags);
+
+ if (!callbacks_performed) {
+ callbacks_performed = 1;
+
+ /* Loop over all of the entries in the table */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ /* If not null, ... */
+ if (scc_callbacks[i]) {
+ scc_callbacks[i] (); /* invoke the callback routine */
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&scc_callbacks_lock, irq_flags);
+
+ return;
+}
+
+/*****************************************************************************/
+/* fn copy_to_scc() */
+/*****************************************************************************/
+/*!
+ * Move data from possibly unaligned source and realign for SCC, possibly
+ * while calculating CRC.
+ *
+ * Multiple calls can be made to this routine (without intervening calls to
+ * #copy_from_scc(), as long as the sum total of bytes copied is a multiple of
+ * four (SCC native word size).
+ *
+ * @param[in] from Location in memory
+ * @param[out] to Location in SCC
+ * @param[in] count_bytes Number of bytes to copy
+ * @param[in,out] crc Pointer to CRC. Initial value must be
+ * #CRC_CCITT_START if this is the start of
+ * message. Output is the resulting (maybe
+ * partial) CRC. If NULL, no crc is calculated.
+ *
+ * @return Zero - success. Non-zero - SCM status bits defining failure.
+ */
+static uint32_t
+copy_to_scc(const uint8_t * from, uint32_t to, unsigned long count_bytes,
+ uint16_t * crc)
+{
+ int i;
+ uint32_t scm_word;
+ uint16_t current_crc = 0; /* local copy for fast access */
+ uint32_t status;
+
+ pr_debug("SCC: copying %ld bytes to 0x%0x.\n", count_bytes, to);
+
+ status = SCC_READ_REGISTER(SCM_ERROR_STATUS) & SCM_ACCESS_ERRORS;
+ if (status != 0) {
+ pr_debug
+ ("SCC copy_to_scc(): Error status detected (before copy):"
+ " %08x\n", status);
+ /* clear out errors left behind by somebody else */
+ SCC_WRITE_REGISTER(SCM_ERROR_STATUS, status);
+ }
+
+ if (crc) {
+ current_crc = *crc;
+ }
+
+ /* Initialize value being built for SCM. If we are starting 'clean',
+ * set it to zero. Otherwise pick up partial value which had been saved
+ * earlier. */
+ if (SCC_BYTE_OFFSET(to) == 0) {
+ scm_word = 0;
+ } else {
+ scm_word = SCC_READ_REGISTER(SCC_WORD_PTR(to)); /* recover */
+ }
+
+ /* Now build up SCM words and write them out when each is full */
+ for (i = 0; i < count_bytes; i++) {
+ uint8_t byte = *from++; /* value from plaintext */
+
+#if defined(__BIG_ENDIAN) || defined(FSL_HAVE_DRYICE)
+ scm_word = (scm_word << 8) | byte; /* add byte to SCM word */
+#else
+ scm_word = (byte << 24) | (scm_word >> 8);
+#endif
+ /* now calculate CCITT CRC */
+ if (crc) {
+ CALC_CRC(byte, current_crc);
+ }
+
+ to++; /* bump location in SCM */
+
+ /* check for full word */
+ if (SCC_BYTE_OFFSET(to) == 0) {
+ SCC_WRITE_REGISTER((uint32_t) (to - 4), scm_word); /* write it out */
+ }
+ }
+
+ /* If at partial word after previous loop, save it in SCM memory for
+ next time. */
+ if (SCC_BYTE_OFFSET(to) != 0) {
+ SCC_WRITE_REGISTER(SCC_WORD_PTR(to), scm_word); /* save */
+ }
+
+ /* Copy CRC back */
+ if (crc) {
+ *crc = current_crc;
+ }
+
+ status = SCC_READ_REGISTER(SCM_ERROR_STATUS) & SCM_ACCESS_ERRORS;
+ if (status != 0) {
+ pr_debug("SCC copy_to_scc(): Error status detected: %08x\n",
+ status);
+ /* Clear any/all bits. */
+ SCC_WRITE_REGISTER(SCM_ERROR_STATUS, status);
+ }
+ return status;
+}
+
+/*****************************************************************************/
+/* fn copy_from_scc() */
+/*****************************************************************************/
+/*!
+ * Move data from aligned 32-bit source and place in (possibly unaligned)
+ * target, and maybe calculate CRC at the same time.
+ *
+ * Multiple calls can be made to this routine (without intervening calls to
+ * #copy_to_scc(), as long as the sum total of bytes copied is be a multiple
+ * of four.
+ *
+ * @param[in] from Location in SCC
+ * @param[out] to Location in memory
+ * @param[in] count_bytes Number of bytes to copy
+ * @param[in,out] crc Pointer to CRC. Initial value must be
+ * #CRC_CCITT_START if this is the start of
+ * message. Output is the resulting (maybe
+ * partial) CRC. If NULL, crc is not calculated.
+ *
+ * @return Zero - success. Non-zero - SCM status bits defining failure.
+ */
+static uint32_t
+copy_from_scc(const uint32_t from, uint8_t * to, unsigned long count_bytes,
+ uint16_t * crc)
+{
+ uint32_t running_from = from;
+ uint32_t scm_word;
+ uint16_t current_crc = 0; /* local copy for fast access */
+ uint32_t status;
+ pr_debug("SCC: copying %ld bytes from 0x%x.\n", count_bytes, from);
+ status = SCC_READ_REGISTER(SCM_ERROR_STATUS) & SCM_ACCESS_ERRORS;
+ if (status != 0) {
+ pr_debug
+ ("SCC copy_from_scc(): Error status detected (before copy):"
+ " %08x\n", status);
+ /* clear out errors left behind by somebody else */
+ SCC_WRITE_REGISTER(SCM_ERROR_STATUS, status);
+ }
+
+ if (crc) {
+ current_crc = *crc;
+ }
+
+ /* Read word which is sitting in SCM memory. Ignore byte offset */
+ scm_word = SCC_READ_REGISTER(SCC_WORD_PTR(running_from));
+
+ /* If necessary, move the 'first' byte into place */
+ if (SCC_BYTE_OFFSET(running_from) != 0) {
+#if defined(__BIG_ENDIAN) || defined(FSL_HAVE_DRYICE)
+ scm_word <<= 8 * SCC_BYTE_OFFSET(running_from);
+#else
+ scm_word >>= 8 * SCC_BYTE_OFFSET(running_from);
+#endif
+ }
+
+ /* Now build up SCM words and write them out when each is full */
+ while (count_bytes--) {
+ uint8_t byte; /* value from plaintext */
+
+#if defined(__BIG_ENDIAN) || defined(FSL_HAVE_DRYICE)
+ byte = (scm_word & 0xff000000) >> 24; /* pull byte out of SCM word */
+ scm_word <<= 8; /* shift over to remove the just-pulled byte */
+#else
+ byte = (scm_word & 0xff);
+ scm_word >>= 8; /* shift over to remove the just-pulled byte */
+#endif
+ *to++ = byte; /* send byte to memory */
+
+ /* now calculate CRC */
+ if (crc) {
+ CALC_CRC(byte, current_crc);
+ }
+
+ running_from++;
+ /* check for empty word */
+ if (count_bytes && SCC_BYTE_OFFSET(running_from) == 0) {
+ /* read one in */
+ scm_word = SCC_READ_REGISTER((uint32_t) running_from);
+ }
+ }
+
+ if (crc) {
+ *crc = current_crc;
+ }
+
+ status = SCC_READ_REGISTER(SCM_ERROR_STATUS) & SCM_ACCESS_ERRORS;
+ if (status != 0) {
+ pr_debug("SCC copy_from_scc(): Error status detected: %08x\n",
+ status);
+ /* Clear any/all bits. */
+ SCC_WRITE_REGISTER(SCM_ERROR_STATUS, status);
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+/* fn scc_strip_padding() */
+/*****************************************************************************/
+/*!
+ * Remove padding from plaintext. Search backwards for #SCC_DRIVER_PAD_CHAR,
+ * verifying that each byte passed over is zero (0). Maximum number of bytes
+ * to examine is 8.
+ *
+ * @param[in] from Pointer to byte after end of message
+ * @param[out] count_bytes_stripped Number of padding bytes removed by this
+ * function.
+ *
+ * @return #SCC_RET_OK if all goes, well, #SCC_RET_FAIL if padding was
+ * not present.
+*/
+static scc_return_t
+scc_strip_padding(uint8_t * from, unsigned *count_bytes_stripped)
+{
+ int i = SCC_BLOCK_SIZE_BYTES();
+ scc_return_t return_code = SCC_RET_VERIFICATION_FAILED;
+
+ /*
+ * Search backwards looking for the magic marker. If it isn't found,
+ * make sure that a 0 byte is there in its place. Stop after the maximum
+ * amount of padding (8 bytes) has been searched);
+ */
+ while (i-- > 0) {
+ if (*--from == SCC_DRIVER_PAD_CHAR) {
+ *count_bytes_stripped = SCC_BLOCK_SIZE_BYTES() - i;
+ return_code = SCC_RET_OK;
+ break;
+ } else if (*from != 0) { /* if not marker, check for 0 */
+ pr_debug("SCC: Found non-zero interim pad: 0x%x\n",
+ *from);
+ break;
+ }
+ }
+
+ return return_code;
+}
+
+/*****************************************************************************/
+/* fn scc_update_state() */
+/*****************************************************************************/
+/*!
+ * Make certain SCC is still running.
+ *
+ * Side effect is to update #scc_availability and, if the state goes to failed,
+ * run #scc_perform_callbacks().
+ *
+ * (If #SCC_BRINGUP is defined, bring SCC to secure state if it is found to be
+ * in health check state)
+ *
+ * @return Current value of #SMN_STATUS register.
+ */
+static uint32_t scc_update_state(void)
+{
+ uint32_t smn_status_register = SMN_STATE_FAIL;
+ int smn_state;
+
+ /* if FAIL or UNIMPLEMENTED, don't bother */
+ if (scc_availability == SCC_STATUS_CHECKING ||
+ scc_availability == SCC_STATUS_OK) {
+
+ smn_status_register = SCC_READ_REGISTER(SMN_STATUS);
+ smn_state = smn_status_register & SMN_STATUS_STATE_MASK;
+
+#ifdef SCC_BRINGUP
+ /* If in Health Check while booting, try to 'bringup' to Secure mode */
+ if (scc_availability == SCC_STATUS_CHECKING &&
+ smn_state == SMN_STATE_HEALTH_CHECK) {
+ /* Code up a simple algorithm for the ASC */
+ SCC_WRITE_REGISTER(SMN_SEQUENCE_START, 0xaaaa);
+ SCC_WRITE_REGISTER(SMN_SEQUENCE_END, 0x5555);
+ SCC_WRITE_REGISTER(SMN_SEQUENCE_CHECK, 0x5555);
+ /* State should be SECURE now */
+ smn_status_register = SCC_READ_REGISTER(SMN_STATUS);
+ smn_state = smn_status_register & SMN_STATUS_STATE_MASK;
+ }
+#endif
+
+ /*
+ * State should be SECURE or NON_SECURE for operation of the part. If
+ * FAIL, mark failed (i.e. limited access to registers). Any other
+ * state, mark unimplemented, as the SCC is unuseable.
+ */
+ if (smn_state == SMN_STATE_SECURE
+ || smn_state == SMN_STATE_NON_SECURE) {
+ /* Healthy */
+ scc_availability = SCC_STATUS_OK;
+ } else if (smn_state == SMN_STATE_FAIL) {
+ scc_availability = SCC_STATUS_FAILED; /* uh oh - unhealthy */
+ scc_perform_callbacks();
+ os_printk(KERN_ERR "SCC: SCC went into FAILED mode\n");
+ } else {
+ /* START, ZEROIZE RAM, HEALTH CHECK, or unknown */
+ scc_availability = SCC_STATUS_UNIMPLEMENTED; /* unuseable */
+ os_printk(KERN_ERR "SCC: SCC declared UNIMPLEMENTED\n");
+ }
+ }
+ /* if availability is initial or ok */
+ return smn_status_register;
+}
+
+/*****************************************************************************/
+/* fn scc_init_ccitt_crc() */
+/*****************************************************************************/
+/*!
+ * Populate the partial CRC lookup table.
+ *
+ * @return none
+ *
+ */
+static void scc_init_ccitt_crc(void)
+{
+ int dividend; /* index for lookup table */
+ uint16_t remainder; /* partial value for a given dividend */
+ int bit; /* index into bits of a byte */
+
+ /*
+ * Compute the remainder of each possible dividend.
+ */
+ for (dividend = 0; dividend < 256; ++dividend) {
+ /*
+ * Start with the dividend followed by zeros.
+ */
+ remainder = dividend << (8);
+
+ /*
+ * Perform modulo-2 division, a bit at a time.
+ */
+ for (bit = 8; bit > 0; --bit) {
+ /*
+ * Try to divide the current data bit.
+ */
+ if (remainder & 0x8000) {
+ remainder = (remainder << 1) ^ CRC_POLYNOMIAL;
+ } else {
+ remainder = (remainder << 1);
+ }
+ }
+
+ /*
+ * Store the result into the table.
+ */
+ scc_crc_lookup_table[dividend] = remainder;
+ }
+
+} /* scc_init_ccitt_crc() */
+
+/*****************************************************************************/
+/* fn grab_config_values() */
+/*****************************************************************************/
+/*!
+ * grab_config_values() will read the SCM Configuration and SMN Status
+ * registers and store away version and size information for later use.
+ *
+ * @return The current value of the SMN Status register.
+ */
+static uint32_t scc_grab_config_values(void)
+{
+ uint32_t config_register;
+ uint32_t smn_status_register = SMN_STATE_FAIL;
+
+ if (scc_availability != SCC_STATUS_UNIMPLEMENTED) {
+ /* access the SCC - these are 'safe' registers */
+ config_register = SCC_READ_REGISTER(SCM_CONFIGURATION);
+ pr_debug("SCC Driver: SCM config is 0x%08x\n", config_register);
+
+ /* Get SMN status and update scc_availability */
+ smn_status_register = scc_update_state();
+ pr_debug("SCC Driver: SMN status is 0x%08x\n",
+ smn_status_register);
+
+ /* save sizes and versions information for later use */
+ scc_configuration.block_size_bytes = (config_register &
+ SCM_CFG_BLOCK_SIZE_MASK)
+ >> SCM_CFG_BLOCK_SIZE_SHIFT;
+
+ scc_configuration.red_ram_size_blocks = (config_register &
+ SCM_CFG_RED_SIZE_MASK)
+ >> SCM_CFG_RED_SIZE_SHIFT;
+
+ scc_configuration.black_ram_size_blocks = (config_register &
+ SCM_CFG_BLACK_SIZE_MASK)
+ >> SCM_CFG_BLACK_SIZE_SHIFT;
+
+ scc_configuration.scm_version = (config_register
+ & SCM_CFG_VERSION_ID_MASK)
+ >> SCM_CFG_VERSION_ID_SHIFT;
+
+ scc_configuration.smn_version = (smn_status_register &
+ SMN_STATUS_VERSION_ID_MASK)
+ >> SMN_STATUS_VERSION_ID_SHIFT;
+
+ if (scc_configuration.scm_version != SCM_VERSION_1) {
+ scc_availability = SCC_STATUS_UNIMPLEMENTED; /* Unknown version */
+ }
+
+ scc_memory_size_bytes = (SCC_BLOCK_SIZE_BYTES() *
+ scc_configuration.
+ black_ram_size_blocks)
+ - SCM_NON_RESERVED_OFFSET;
+
+ /* This last is for driver consumption only */
+ scm_highest_memory_address = SCM_BLACK_MEMORY +
+ (SCC_BLOCK_SIZE_BYTES() *
+ scc_configuration.black_ram_size_blocks);
+ }
+
+ return smn_status_register;
+} /* grab_config_values */
+
+/*****************************************************************************/
+/* fn setup_interrupt_handling() */
+/*****************************************************************************/
+/*!
+ * Register the SCM and SMN interrupt handlers.
+ *
+ * Called from #scc_init()
+ *
+ * @return 0 on success
+ */
+static int setup_interrupt_handling(void)
+{
+ int smn_error_code = -1;
+ int scm_error_code = -1;
+
+ /* Disnable SCM interrupts */
+ SCC_WRITE_REGISTER(SCM_INTERRUPT_CTRL,
+ SCM_INTERRUPT_CTRL_CLEAR_INTERRUPT
+ | SCM_INTERRUPT_CTRL_MASK_INTERRUPTS);
+
+#ifdef USE_SMN_INTERRUPT
+ /* Install interrupt service routine for SMN. */
+ smn_error_code = os_register_interrupt(SCC_DRIVER_NAME,
+ INT_SCC_SMN, scc_irq);
+ if (smn_error_code != 0) {
+ os_printk
+ ("SCC Driver: Error installing SMN Interrupt Handler: %d\n",
+ smn_error_code);
+ } else {
+ smn_irq_set = 1; /* remember this for cleanup */
+ /* Enable SMN interrupts */
+ SCC_WRITE_REGISTER(SMN_COMMAND,
+ SMN_COMMAND_CLEAR_INTERRUPT |
+ SMN_COMMAND_ENABLE_INTERRUPT);
+ }
+#else
+ smn_error_code = 0; /* no problems... will handle later */
+#endif
+
+ /*
+ * Install interrupt service routine for SCM (or both together).
+ */
+ scm_error_code = os_register_interrupt(SCC_DRIVER_NAME,
+ INT_SCC_SCM, scc_irq);
+ if (scm_error_code != 0) {
+#ifndef MXC
+ os_printk
+ ("SCC Driver: Error installing SCM Interrupt Handler: %d\n",
+ scm_error_code);
+#else
+ os_printk
+ ("SCC Driver: Error installing SCC Interrupt Handler: %d\n",
+ scm_error_code);
+#endif
+ } else {
+ scm_irq_set = 1; /* remember this for cleanup */
+#if defined(USE_SMN_INTERRUPT) && !defined(NO_SMN_INTERRUPT)
+ /* Enable SMN interrupts */
+ SCC_WRITE_REGISTER(SMN_COMMAND,
+ SMN_COMMAND_CLEAR_INTERRUPT |
+ SMN_COMMAND_ENABLE_INTERRUPT);
+#endif
+ }
+
+ /* Return an error if one was encountered */
+ return scm_error_code ? scm_error_code : smn_error_code;
+} /* setup_interrupt_handling */
+
+/*****************************************************************************/
+/* fn scc_do_crypto() */
+/*****************************************************************************/
+/*! Have the SCM perform the crypto function.
+ *
+ * Set up length register, and the store @c scm_control into control register
+ * to kick off the operation. Wait for completion, gather status, clear
+ * interrupt / status.
+ *
+ * @param byte_count number of bytes to perform in this operation
+ * @param scm_control Bit values to be set in @c SCM_CONTROL register
+ *
+ * @return 0 on success, value of #SCM_ERROR_STATUS on failure
+ */
+static uint32_t scc_do_crypto(int byte_count, uint32_t scm_control)
+{
+ int block_count = byte_count / SCC_BLOCK_SIZE_BYTES();
+ uint32_t crypto_status;
+
+ /* clear any outstanding flags */
+ SCC_WRITE_REGISTER(SCM_INTERRUPT_CTRL,
+ SCM_INTERRUPT_CTRL_CLEAR_INTERRUPT
+ | SCM_INTERRUPT_CTRL_MASK_INTERRUPTS);
+
+ /* In length register, 0 means 1, etc. */
+ SCC_WRITE_REGISTER(SCM_LENGTH, block_count - 1);
+
+ /* set modes and kick off the operation */
+ SCC_WRITE_REGISTER(SCM_CONTROL, scm_control);
+
+ scc_wait_completion();
+
+ /* Mask for done and error bits */
+ crypto_status = SCC_READ_REGISTER(SCM_STATUS)
+ & (SCM_STATUS_CIPHERING_DONE
+ | SCM_STATUS_LENGTH_ERROR | SCM_STATUS_INTERNAL_ERROR);
+
+ /* Only done bit should be on */
+ if (crypto_status != SCM_STATUS_CIPHERING_DONE) {
+ /* Replace with error status instead */
+ crypto_status = SCC_READ_REGISTER(SCM_ERROR_STATUS);
+ pr_debug("SCM Failure: 0x%x\n", crypto_status);
+ if (crypto_status == 0) {
+ /* That came up 0. Turn on arbitrary bit to signal error. */
+ crypto_status = SCM_ERR_INTERNAL_ERROR;
+ }
+ } else {
+ crypto_status = 0;
+ }
+
+ pr_debug("SCC: Done waiting.\n");
+
+ /* Clear out any status. */
+ SCC_WRITE_REGISTER(SCM_INTERRUPT_CTRL,
+ SCM_INTERRUPT_CTRL_CLEAR_INTERRUPT
+ | SCM_INTERRUPT_CTRL_MASK_INTERRUPTS);
+
+ /* And clear any error status */
+ SCC_WRITE_REGISTER(SCM_ERROR_STATUS, 0);
+
+ return crypto_status;
+}
+
+/*****************************************************************************/
+/* fn scc_encrypt() */
+/*****************************************************************************/
+/*!
+ * Perform an encryption on the input. If @c verify_crc is true, a CRC must be
+ * calculated on the plaintext, and appended, with padding, before computing
+ * the ciphertext.
+ *
+ * @param[in] count_in_bytes Count of bytes of plaintext
+ * @param[in] data_in Pointer to the plaintext
+ * @param[in] scm_control Bit values for the SCM_CONTROL register
+ * @param[in,out] data_out Pointer for storing ciphertext
+ * @param[in] add_crc Flag for computing CRC - 0 no, else yes
+ * @param[in,out] count_out_bytes Number of bytes available at @c data_out
+ */
+static scc_return_t
+scc_encrypt(uint32_t count_in_bytes, const uint8_t * data_in,
+ uint32_t scm_control,
+ uint8_t * data_out, int add_crc, unsigned long *count_out_bytes)
+
+{
+ scc_return_t return_code = SCC_RET_FAIL; /* initialised for failure */
+ uint32_t input_bytes_left = count_in_bytes; /* local copy */
+ uint32_t output_bytes_copied = 0; /* running total */
+ uint32_t bytes_to_process; /* multi-purpose byte counter */
+ uint16_t crc = CRC_CCITT_START; /* running CRC value */
+ crc_t *crc_ptr = NULL; /* Reset if CRC required */
+ /* byte address into SCM RAM */
+ uint32_t scm_location = SCM_RED_MEMORY + SCM_NON_RESERVED_OFFSET;
+ /* free RED RAM */
+ uint32_t scm_bytes_remaining = scc_memory_size_bytes;
+ /* CRC+padding holder */
+ uint8_t padding_buffer[PADDING_BUFFER_MAX_BYTES];
+ unsigned padding_byte_count = 0; /* Reset if padding required */
+ uint32_t scm_error_status = 0; /* No known SCM error initially */
+ uint32_t i; /* Counter for clear data loop */
+ uint32_t dirty_bytes; /* Number of bytes of memory used
+ temporarily during encryption,
+ which need to be wiped after
+ completion of the operation. */
+
+ /* Set location of CRC and prepare padding bytes if required */
+ if (add_crc != 0) {
+ crc_ptr = &crc;
+ padding_byte_count = SCC_BLOCK_SIZE_BYTES()
+ - (count_in_bytes +
+ CRC_SIZE_BYTES) % SCC_BLOCK_SIZE_BYTES();
+ memcpy(padding_buffer + CRC_SIZE_BYTES, scc_block_padding,
+ padding_byte_count);
+ }
+
+ /* Process remaining input or padding data */
+ while (input_bytes_left > 0) {
+
+ /* Determine how much work to do this pass */
+ bytes_to_process = (input_bytes_left > scm_bytes_remaining) ?
+ scm_bytes_remaining : input_bytes_left;
+
+ /* Copy plaintext into SCM RAM, calculating CRC if required */
+ copy_to_scc(data_in, scm_location, bytes_to_process, crc_ptr);
+
+ /* Adjust pointers & counters */
+ input_bytes_left -= bytes_to_process;
+ data_in += bytes_to_process;
+ scm_location += bytes_to_process;
+ scm_bytes_remaining -= bytes_to_process;
+
+ /* Add CRC and padding after the last byte is copied if required */
+ if ((input_bytes_left == 0) && (crc_ptr != NULL)) {
+
+ /* Copy CRC into padding buffer MSB first */
+ padding_buffer[0] = (crc >> 8) & 0xFF;
+ padding_buffer[1] = crc & 0xFF;
+
+ /* Reset pointers and counter */
+ data_in = padding_buffer;
+ input_bytes_left = CRC_SIZE_BYTES + padding_byte_count;
+ crc_ptr = NULL; /* CRC no longer required */
+
+ /* Go round loop again to copy CRC and padding to SCM */
+ continue;
+ }
+
+ /* if no input and crc_ptr */
+ /* Now have block-sized plaintext in SCM to encrypt */
+ /* Encrypt plaintext; exit loop on error */
+ bytes_to_process = scm_location -
+ (SCM_RED_MEMORY + SCM_NON_RESERVED_OFFSET);
+
+ if (output_bytes_copied + bytes_to_process > *count_out_bytes) {
+ return_code = SCC_RET_INSUFFICIENT_SPACE;
+ scm_error_status = -1; /* error signal */
+ pr_debug
+ ("SCC: too many ciphertext bytes for space available\n");
+ break;
+ }
+ pr_debug("SCC: Starting encryption. %x for %d bytes (%p/%p)\n",
+ scm_control, bytes_to_process,
+ (void *)SCC_READ_REGISTER(SCM_RED_START),
+ (void *)SCC_READ_REGISTER(SCM_BLACK_START));
+ scm_error_status = scc_do_crypto(bytes_to_process, scm_control);
+ if (scm_error_status != 0) {
+ break;
+ }
+
+ /* Copy out ciphertext */
+ copy_from_scc(SCM_BLACK_MEMORY + SCM_NON_RESERVED_OFFSET,
+ data_out, bytes_to_process, NULL);
+
+ /* Adjust pointers and counters for next loop */
+ output_bytes_copied += bytes_to_process;
+ data_out += bytes_to_process;
+ scm_location = SCM_RED_MEMORY + SCM_NON_RESERVED_OFFSET;
+ scm_bytes_remaining = scc_memory_size_bytes;
+
+ } /* input_bytes_left > 0 */
+ /* Clear all red and black memory used during ephemeral encryption */
+ dirty_bytes = (count_in_bytes > scc_memory_size_bytes) ?
+ scc_memory_size_bytes : count_in_bytes;
+
+ for (i = 0; i < dirty_bytes; i += 4) {
+ SCC_WRITE_REGISTER(SCM_RED_MEMORY + SCM_NON_RESERVED_OFFSET + i,
+ 0);
+ SCC_WRITE_REGISTER(SCM_BLACK_MEMORY + SCM_NON_RESERVED_OFFSET +
+ i, 0);
+ }
+
+ /* If no SCM error, set OK status and save ouput byte count */
+ if (scm_error_status == 0) {
+ return_code = SCC_RET_OK;
+ *count_out_bytes = output_bytes_copied;
+ }
+
+ return return_code;
+} /* scc_encrypt */
+
+/*****************************************************************************/
+/* fn scc_decrypt() */
+/*****************************************************************************/
+/*!
+ * Perform a decryption on the input. If @c verify_crc is true, the last block
+ * (maybe the two last blocks) is special - it should contain a CRC and
+ * padding. These must be stripped and verified.
+ *
+ * @param[in] count_in_bytes Count of bytes of ciphertext
+ * @param[in] data_in Pointer to the ciphertext
+ * @param[in] scm_control Bit values for the SCM_CONTROL register
+ * @param[in,out] data_out Pointer for storing plaintext
+ * @param[in] verify_crc Flag for running CRC - 0 no, else yes
+ * @param[in,out] count_out_bytes Number of bytes available at @c data_out
+
+ */
+static scc_return_t
+scc_decrypt(uint32_t count_in_bytes, const uint8_t * data_in,
+ uint32_t scm_control,
+ uint8_t * data_out, int verify_crc, unsigned long *count_out_bytes)
+{
+ scc_return_t return_code = SCC_RET_FAIL;
+ uint32_t bytes_left = count_in_bytes; /* local copy */
+ uint32_t bytes_copied = 0; /* running total of bytes going to user */
+ uint32_t bytes_to_copy = 0; /* Number in this encryption 'chunk' */
+ uint16_t crc = CRC_CCITT_START; /* running CRC value */
+ /* next target for ctext */
+ uint32_t scm_location = SCM_BLACK_MEMORY + SCM_NON_RESERVED_OFFSET;
+ unsigned padding_byte_count; /* number of bytes of padding stripped */
+ uint8_t last_two_blocks[2 * SCC_BLOCK_SIZE_BYTES()]; /* temp */
+ uint32_t scm_error_status = 0; /* register value */
+ uint32_t i; /* Counter for clear data loop */
+ uint32_t dirty_bytes; /* Number of bytes of memory used
+ temporarily during decryption,
+ which need to be wiped after
+ completion of the operation. */
+
+ scm_control |= SCM_DECRYPT_MODE;
+
+ if (verify_crc) {
+ /* Save last two blocks (if there are at least two) of ciphertext for
+ special treatment. */
+ bytes_left -= SCC_BLOCK_SIZE_BYTES();
+ if (bytes_left >= SCC_BLOCK_SIZE_BYTES()) {
+ bytes_left -= SCC_BLOCK_SIZE_BYTES();
+ }
+ }
+
+ /* Copy ciphertext into SCM BLACK memory */
+ while (bytes_left && scm_error_status == 0) {
+
+ /* Determine how much work to do this pass */
+ if (bytes_left > (scc_memory_size_bytes)) {
+ bytes_to_copy = scc_memory_size_bytes;
+ } else {
+ bytes_to_copy = bytes_left;
+ }
+
+ if (bytes_copied + bytes_to_copy > *count_out_bytes) {
+ scm_error_status = -1;
+ break;
+ }
+ copy_to_scc(data_in, scm_location, bytes_to_copy, NULL);
+ data_in += bytes_to_copy; /* move pointer */
+
+ pr_debug("SCC: Starting decryption of %d bytes.\n",
+ bytes_to_copy);
+
+ /* Do the work, wait for completion */
+ scm_error_status = scc_do_crypto(bytes_to_copy, scm_control);
+
+ copy_from_scc(SCM_RED_MEMORY + SCM_NON_RESERVED_OFFSET,
+ data_out, bytes_to_copy, &crc);
+ bytes_copied += bytes_to_copy;
+ data_out += bytes_to_copy;
+ scm_location = SCM_BLACK_MEMORY + SCM_NON_RESERVED_OFFSET;
+
+ /* Do housekeeping */
+ bytes_left -= bytes_to_copy;
+
+ } /* while bytes_left */
+
+ /* At this point, either the process is finished, or this is verify mode */
+
+ if (scm_error_status == 0) {
+ if (!verify_crc) {
+ *count_out_bytes = bytes_copied;
+ return_code = SCC_RET_OK;
+ } else {
+ /* Verify mode. There are one or two blocks of unprocessed
+ * ciphertext sitting at data_in. They need to be moved to the
+ * SCM, decrypted, searched to remove padding, then the plaintext
+ * copied back to the user (while calculating CRC, of course).
+ */
+
+ /* Calculate ciphertext still left */
+ bytes_to_copy = count_in_bytes - bytes_copied;
+
+ copy_to_scc(data_in, scm_location, bytes_to_copy, NULL);
+ data_in += bytes_to_copy; /* move pointer */
+
+ pr_debug("SCC: Finishing decryption (%d bytes).\n",
+ bytes_to_copy);
+
+ /* Do the work, wait for completion */
+ scm_error_status =
+ scc_do_crypto(bytes_to_copy, scm_control);
+
+ if (scm_error_status == 0) {
+ /* Copy decrypted data back from SCM RED memory */
+ copy_from_scc(SCM_RED_MEMORY +
+ SCM_NON_RESERVED_OFFSET,
+ last_two_blocks, bytes_to_copy,
+ NULL);
+
+ /* (Plaintext) + crc + padding should be in temp buffer */
+ if (scc_strip_padding
+ (last_two_blocks + bytes_to_copy,
+ &padding_byte_count) == SCC_RET_OK) {
+ bytes_to_copy -=
+ padding_byte_count + CRC_SIZE_BYTES;
+
+ /* verify enough space in user buffer */
+ if (bytes_copied + bytes_to_copy <=
+ *count_out_bytes) {
+ int i = 0;
+
+ /* Move out last plaintext and calc CRC */
+ while (i < bytes_to_copy) {
+ CALC_CRC(last_two_blocks
+ [i], crc);
+ *data_out++ =
+ last_two_blocks
+ [i++];
+ bytes_copied++;
+ }
+
+ /* Verify the CRC by running over itself */
+ CALC_CRC(last_two_blocks
+ [bytes_to_copy], crc);
+ CALC_CRC(last_two_blocks
+ [bytes_to_copy + 1],
+ crc);
+ if (crc == 0) {
+ /* Just fine ! */
+ *count_out_bytes =
+ bytes_copied;
+ return_code =
+ SCC_RET_OK;
+ } else {
+ return_code =
+ SCC_RET_VERIFICATION_FAILED;
+ pr_debug
+ ("SCC: CRC values are %04x, %02x%02x\n",
+ crc,
+ last_two_blocks
+ [bytes_to_copy],
+ last_two_blocks
+ [bytes_to_copy +
+ 1]);
+ }
+ } /* if space available */
+ } /* if scc_strip_padding... */
+ else {
+ /* bad padding means bad verification */
+ return_code =
+ SCC_RET_VERIFICATION_FAILED;
+ }
+ }
+ /* scm_error_status == 0 */
+ } /* verify_crc */
+ }
+
+ /* scm_error_status == 0 */
+ /* Clear all red and black memory used during ephemeral decryption */
+ dirty_bytes = (count_in_bytes > scc_memory_size_bytes) ?
+ scc_memory_size_bytes : count_in_bytes;
+
+ for (i = 0; i < dirty_bytes; i += 4) {
+ SCC_WRITE_REGISTER(SCM_RED_MEMORY + SCM_NON_RESERVED_OFFSET + i,
+ 0);
+ SCC_WRITE_REGISTER(SCM_BLACK_MEMORY + SCM_NON_RESERVED_OFFSET +
+ i, 0);
+ }
+ return return_code;
+} /* scc_decrypt */
+
+/*****************************************************************************/
+/* fn scc_alloc_slot() */
+/*****************************************************************************/
+/*!
+ * Allocate a key slot to fit the requested size.
+ *
+ * @param value_size_bytes Size of the key or other secure data
+ * @param owner_id Value to tie owner to slot
+ * @param[out] slot Handle to access or deallocate slot
+ *
+ * @return SCC_RET_OK on success, SCC_RET_INSUFFICIENT_SPACE if not slots of
+ * requested size are available.
+ */
+scc_return_t
+scc_alloc_slot(uint32_t value_size_bytes, uint64_t owner_id, uint32_t * slot)
+{
+ scc_return_t status = SCC_RET_FAIL;
+ unsigned long irq_flags;
+
+ if (scc_availability != SCC_STATUS_OK) {
+ goto out;
+ }
+ /* ACQUIRE LOCK to prevent others from using SCC crypto */
+ spin_lock_irqsave(&scc_crypto_lock, irq_flags);
+
+ pr_debug("SCC: Allocating %d-byte slot for 0x%Lx\n",
+ value_size_bytes, owner_id);
+
+ if ((value_size_bytes != 0) && (value_size_bytes <= SCC_MAX_KEY_SIZE)) {
+ int i;
+
+ for (i = 0; i < SCC_KEY_SLOTS; i++) {
+ if (scc_key_info[i].status == 0) {
+ scc_key_info[i].owner_id = owner_id;
+ scc_key_info[i].length = value_size_bytes;
+ scc_key_info[i].status = 1; /* assigned! */
+ *slot = i;
+ status = SCC_RET_OK;
+ break; /* exit 'for' loop */
+ }
+ }
+
+ if (status != SCC_RET_OK) {
+ status = SCC_RET_INSUFFICIENT_SPACE;
+ } else {
+ pr_debug("SCC: Allocated slot %d (0x%Lx)\n", i,
+ owner_id);
+ }
+ }
+
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+
+ out:
+ return status;
+}
+
+/*****************************************************************************/
+/* fn verify_slot_access() */
+/*****************************************************************************/
+inline static scc_return_t
+verify_slot_access(uint64_t owner_id, uint32_t slot, uint32_t access_len)
+{
+ scc_return_t status = SCC_RET_FAIL;
+ if (scc_availability != SCC_STATUS_OK) {
+ goto out;
+ }
+
+ if ((slot < SCC_KEY_SLOTS) && scc_key_info[slot].status
+ && (scc_key_info[slot].owner_id == owner_id)
+ && (access_len <= SCC_KEY_SLOT_SIZE)) {
+ status = SCC_RET_OK;
+ pr_debug("SCC: Verify on slot %d succeeded\n", slot);
+ } else {
+ if (slot >= SCC_KEY_SLOTS) {
+ pr_debug("SCC: Verify on bad slot (%d) failed\n", slot);
+ } else if (scc_key_info[slot].status) {
+ pr_debug("SCC: Verify on slot %d failed (%Lx) \n", slot,
+ owner_id);
+ } else {
+ pr_debug
+ ("SCC: Verify on slot %d failed: not allocated\n",
+ slot);
+ }
+ }
+
+ out:
+ return status;
+}
+
+scc_return_t
+scc_verify_slot_access(uint64_t owner_id, uint32_t slot, uint32_t access_len)
+{
+ return verify_slot_access(owner_id, slot, access_len);
+}
+
+/*****************************************************************************/
+/* fn scc_dealloc_slot() */
+/*****************************************************************************/
+scc_return_t scc_dealloc_slot(uint64_t owner_id, uint32_t slot)
+{
+ scc_return_t status;
+ unsigned long irq_flags;
+ int i;
+
+ /* ACQUIRE LOCK to prevent others from using SCC crypto */
+ spin_lock_irqsave(&scc_crypto_lock, irq_flags);
+
+ status = verify_slot_access(owner_id, slot, 0);
+
+ if (status == SCC_RET_OK) {
+ scc_key_info[slot].owner_id = 0;
+ scc_key_info[slot].status = 0; /* unassign */
+
+ /* clear old info */
+ for (i = 0; i < SCC_KEY_SLOT_SIZE; i += 4) {
+ SCC_WRITE_REGISTER(SCM_RED_MEMORY +
+ scc_key_info[slot].offset + i, 0);
+ SCC_WRITE_REGISTER(SCM_BLACK_MEMORY +
+ scc_key_info[slot].offset + i, 0);
+ }
+ pr_debug("SCC: Deallocated slot %d\n", slot);
+ }
+
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+
+ return status;
+}
+
+/*****************************************************************************/
+/* fn scc_load_slot() */
+/*****************************************************************************/
+/*!
+ * Load a value into a slot.
+ *
+ * @param owner_id Value of owner of slot
+ * @param slot Handle of slot
+ * @param key_data Data to load into the slot
+ * @param key_length Length, in bytes, of @c key_data to copy to SCC.
+ *
+ * @return SCC_RET_OK on success. SCC_RET_FAIL will be returned if slot
+ * specified cannot be accessed for any reason, or SCC_RET_INSUFFICIENT_SPACE
+ * if @c key_length exceeds the size of the slot.
+ */
+scc_return_t
+scc_load_slot(uint64_t owner_id, uint32_t slot, const uint8_t * key_data,
+ uint32_t key_length)
+{
+ scc_return_t status;
+ unsigned long irq_flags;
+
+ /* ACQUIRE LOCK to prevent others from using SCC crypto */
+ spin_lock_irqsave(&scc_crypto_lock, irq_flags);
+
+ status = verify_slot_access(owner_id, slot, key_length);
+ if ((status == SCC_RET_OK) && (key_data != NULL)) {
+ status = SCC_RET_FAIL; /* reset expectations */
+
+ if (key_length > SCC_KEY_SLOT_SIZE) {
+ pr_debug
+ ("SCC: scc_load_slot() rejecting key of %d bytes.\n",
+ key_length);
+ status = SCC_RET_INSUFFICIENT_SPACE;
+ } else {
+ if (copy_to_scc(key_data,
+ SCM_RED_MEMORY +
+ scc_key_info[slot].offset, key_length,
+ NULL)) {
+ pr_debug("SCC: RED copy_to_scc() failed for"
+ " scc_load_slot()\n");
+ } else {
+ if ((key_length % 4) != 0) {
+ uint32_t zeros = 0;
+
+ /* zero-pad to get remainder bytes in correct place */
+ copy_to_scc((uint8_t *) & zeros,
+ SCM_RED_MEMORY
+ +
+ scc_key_info[slot].offset +
+ key_length,
+ 4 - (key_length % 4), NULL);
+ }
+ status = SCC_RET_OK;
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+
+ return status;
+} /* scc_load_slot */
+
+scc_return_t
+scc_read_slot(uint64_t owner_id, uint32_t slot, uint32_t key_length,
+ uint8_t * key_data)
+{
+ scc_return_t status;
+ unsigned long irq_flags;
+
+ /* ACQUIRE LOCK to prevent others from using SCC crypto */
+ spin_lock_irqsave(&scc_crypto_lock, irq_flags);
+
+ status = verify_slot_access(owner_id, slot, key_length);
+ if ((status == SCC_RET_OK) && (key_data != NULL)) {
+ status = SCC_RET_FAIL; /* reset expectations */
+
+ if (key_length > SCC_KEY_SLOT_SIZE) {
+ pr_debug
+ ("SCC: scc_read_slot() rejecting key of %d bytes.\n",
+ key_length);
+ status = SCC_RET_INSUFFICIENT_SPACE;
+ } else {
+ if (copy_from_scc
+ (SCM_RED_MEMORY + scc_key_info[slot].offset,
+ key_data, key_length, NULL)) {
+ pr_debug("SCC: RED copy_from_scc() failed for"
+ " scc_read_slot()\n");
+ } else {
+ status = SCC_RET_OK;
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+
+ return status;
+} /* scc_read_slot */
+
+/*****************************************************************************/
+/* fn scc_encrypt_slot() */
+/*****************************************************************************/
+/*!
+ * Encrypt the key data stored in a slot.
+ *
+ * @param owner_id Value of owner of slot
+ * @param slot Handle of slot
+ * @param length Length, in bytes, of @c black_data
+ * @param black_data Location to store result of encrypting RED data in slot
+ *
+ * @return SCC_RET_OK on success, SCC_RET_FAIL if slot specified cannot be
+ * accessed for any reason.
+ */
+scc_return_t scc_encrypt_slot(uint64_t owner_id, uint32_t slot,
+ uint32_t length, uint8_t * black_data)
+{
+ unsigned long irq_flags;
+ scc_return_t status;
+ uint32_t crypto_status;
+ uint32_t slot_offset =
+ scc_key_info[slot].offset / SCC_BLOCK_SIZE_BYTES();
+
+ /* ACQUIRE LOCK to prevent others from using crypto or releasing slot */
+ spin_lock_irqsave(&scc_crypto_lock, irq_flags);
+
+ status = verify_slot_access(owner_id, slot, length);
+ if (status == SCC_RET_OK) {
+ SCC_WRITE_REGISTER(SCM_BLACK_START, slot_offset);
+ SCC_WRITE_REGISTER(SCM_RED_START, slot_offset);
+
+ /* Use OwnerID as CBC IV to tie Owner to data */
+ SCC_WRITE_REGISTER(SCM_INIT_VECTOR_0, *(uint32_t *) & owner_id);
+ SCC_WRITE_REGISTER(SCM_INIT_VECTOR_1,
+ *(((uint32_t *) & owner_id) + 1));
+
+ /* Set modes and kick off the encryption */
+ crypto_status = scc_do_crypto(length,
+ SCM_CONTROL_START_CIPHER |
+ SCM_CBC_MODE);
+
+ if (crypto_status != 0) {
+ pr_debug("SCM encrypt red crypto failure: 0x%x\n",
+ crypto_status);
+ } else {
+
+ /* Give blob back to caller */
+ if (!copy_from_scc
+ (SCM_BLACK_MEMORY + scc_key_info[slot].offset,
+ black_data, length, NULL)) {
+ status = SCC_RET_OK;
+ pr_debug("SCC: Encrypted slot %d\n", slot);
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+
+ return status;
+}
+
+/*****************************************************************************/
+/* fn scc_decrypt_slot() */
+/*****************************************************************************/
+/*!
+ * Decrypt some black data and leave result in the slot.
+ *
+ * @param owner_id Value of owner of slot
+ * @param slot Handle of slot
+ * @param length Length, in bytes, of @c black_data
+ * @param black_data Location of data to dencrypt and store in slot
+ *
+ * @return SCC_RET_OK on success, SCC_RET_FAIL if slot specified cannot be
+ * accessed for any reason.
+ */
+scc_return_t scc_decrypt_slot(uint64_t owner_id, uint32_t slot,
+ uint32_t length, const uint8_t * black_data)
+{
+ unsigned long irq_flags;
+ scc_return_t status;
+ uint32_t crypto_status;
+ uint32_t slot_offset =
+ scc_key_info[slot].offset / SCC_BLOCK_SIZE_BYTES();
+
+ /* ACQUIRE LOCK to prevent others from using crypto or releasing slot */
+ spin_lock_irqsave(&scc_crypto_lock, irq_flags);
+
+ status = verify_slot_access(owner_id, slot, length);
+ if (status == SCC_RET_OK) {
+ status = SCC_RET_FAIL; /* reset expectations */
+
+ /* Place black key in to BLACK RAM and set up the SCC */
+ copy_to_scc(black_data,
+ SCM_BLACK_MEMORY + scc_key_info[slot].offset,
+ length, NULL);
+
+ SCC_WRITE_REGISTER(SCM_BLACK_START, slot_offset);
+ SCC_WRITE_REGISTER(SCM_RED_START, slot_offset);
+
+ /* Use OwnerID as CBC IV to tie Owner to data */
+ SCC_WRITE_REGISTER(SCM_INIT_VECTOR_0, *(uint32_t *) & owner_id);
+ SCC_WRITE_REGISTER(SCM_INIT_VECTOR_1,
+ *(((uint32_t *) & owner_id) + 1));
+
+ /* Set modes and kick off the decryption */
+ crypto_status = scc_do_crypto(length,
+ SCM_CONTROL_START_CIPHER
+ | SCM_CBC_MODE |
+ SCM_DECRYPT_MODE);
+
+ if (crypto_status != 0) {
+ pr_debug("SCM decrypt black crypto failure: 0x%x\n",
+ crypto_status);
+ } else {
+ status = SCC_RET_OK;
+ }
+ }
+
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+
+ return status;
+}
+
+/*****************************************************************************/
+/* fn scc_get_slot_info() */
+/*****************************************************************************/
+/*!
+ * Determine address and value length for a give slot.
+ *
+ * @param owner_id Value of owner of slot
+ * @param slot Handle of slot
+ * @param address Location to store kernel address of slot data
+ * @param value_size_bytes Location to store allocated length of data in slot.
+ * May be NULL if value is not needed by caller.
+ * @param slot_size_bytes Location to store max length data in slot
+ * May be NULL if value is not needed by caller.
+ *
+ * @return SCC_RET_OK or error indication
+ */
+scc_return_t
+scc_get_slot_info(uint64_t owner_id, uint32_t slot, uint32_t * address,
+ uint32_t * value_size_bytes, uint32_t * slot_size_bytes)
+{
+ scc_return_t status = verify_slot_access(owner_id, slot, 0);
+
+ if (status == SCC_RET_OK) {
+ *address =
+ SCC_BASE + SCM_RED_MEMORY + scc_key_info[slot].offset;
+ if (value_size_bytes != NULL) {
+ *value_size_bytes = scc_key_info[slot].length;
+ }
+ if (slot_size_bytes != NULL) {
+ *slot_size_bytes = SCC_KEY_SLOT_SIZE;
+ }
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+/* fn scc_wait_completion() */
+/*****************************************************************************/
+/*!
+ * Poll looking for end-of-cipher indication. Only used
+ * if @c SCC_SCM_SLEEP is not defined.
+ *
+ * @internal
+ *
+ * On a Tahiti, crypto under 230 or so bytes is done after the first loop, all
+ * the way up to five sets of spins for 1024 bytes. (8- and 16-byte functions
+ * are done when we first look. Zeroizing takes one pass around.
+ */
+static void scc_wait_completion(void)
+{
+ int i = 0;
+
+ /* check for completion by polling */
+ while (!is_cipher_done() && (i++ < SCC_CIPHER_MAX_POLL_COUNT)) {
+ udelay(10);
+ }
+ pr_debug("SCC: Polled DONE %d times\n", i);
+} /* scc_wait_completion() */
+
+/*****************************************************************************/
+/* fn is_cipher_done() */
+/*****************************************************************************/
+/*!
+ * This function returns non-zero if SCM Status register indicates
+ * that a cipher has terminated or some other interrupt-generating
+ * condition has occurred.
+ */
+static int is_cipher_done(void)
+{
+ register uint32_t scm_status;
+ register int cipher_done;
+
+ scm_status = SCC_READ_REGISTER(SCM_STATUS);
+
+ /*
+ * Done when 'SCM is currently performing a function' bits are zero
+ */
+ cipher_done = !(scm_status & (SCM_STATUS_ZEROIZING |
+ SCM_STATUS_CIPHERING));
+
+ return cipher_done;
+} /* is_cipher_done() */
+
+/*****************************************************************************/
+/* fn offset_within_smn() */
+/*****************************************************************************/
+/*!
+ * Check that the offset is with the bounds of the SMN register set.
+ *
+ * @param[in] register_offset register offset of SMN.
+ *
+ * @return 1 if true, 0 if false (not within SMN)
+ */
+static inline int offset_within_smn(uint32_t register_offset)
+{
+ return register_offset >= SMN_STATUS && register_offset <= SMN_TIMER;
+}
+
+/*****************************************************************************/
+/* fn offset_within_scm() */
+/*****************************************************************************/
+/*!
+ * Check that the offset is with the bounds of the SCM register set.
+ *
+ * @param[in] register_offset Register offset of SCM
+ *
+ * @return 1 if true, 0 if false (not within SCM)
+ */
+static inline int offset_within_scm(uint32_t register_offset)
+{
+ return (register_offset >= SCM_RED_START)
+ && (register_offset < scm_highest_memory_address);
+ /* Although this would cause trouble for zeroize testing, this change would
+ * close a security whole which currently allows any kernel program to access
+ * any location in RED RAM. Perhaps enforce in non-SCC_DEBUG compiles?
+ && (register_offset <= SCM_INIT_VECTOR_1); */
+}
+
+/*****************************************************************************/
+/* fn check_register_accessible() */
+/*****************************************************************************/
+/*!
+ * Given the current SCM and SMN status, verify that access to the requested
+ * register should be OK.
+ *
+ * @param[in] register_offset register offset within SCC
+ * @param[in] smn_status recent value from #SMN_STATUS
+ * @param[in] scm_status recent value from #SCM_STATUS
+ *
+ * @return #SCC_RET_OK if ok, #SCC_RET_FAIL if not
+ */
+static scc_return_t
+check_register_accessible(uint32_t register_offset, uint32_t smn_status,
+ uint32_t scm_status)
+{
+ int error_code = SCC_RET_FAIL;
+
+ /* Verify that the register offset passed in is not among the verboten set
+ * if the SMN is in Fail mode.
+ */
+ if (offset_within_smn(register_offset)) {
+ if ((smn_status & SMN_STATUS_STATE_MASK) == SMN_STATE_FAIL) {
+ if (!((register_offset == SMN_STATUS) ||
+ (register_offset == SMN_COMMAND) ||
+ (register_offset == SMN_DEBUG_DETECT_STAT))) {
+ pr_debug
+ ("SCC Driver: Note: Security State is in FAIL state.\n");
+ } /* register not a safe one */
+ else {
+ /* SMN is in FAIL, but register is a safe one */
+ error_code = SCC_RET_OK;
+ }
+ } /* State is FAIL */
+ else {
+ /* State is not fail. All registers accessible. */
+ error_code = SCC_RET_OK;
+ }
+ }
+ /* offset within SMN */
+ /* Not SCM register. Check for SCM busy. */
+ else if (offset_within_scm(register_offset)) {
+ /* This is the 'cannot access' condition in the SCM */
+ if ((scm_status & SCM_STATUS_BUSY)
+ /* these are always available - rest fail on busy */
+ && !((register_offset == SCM_STATUS) ||
+ (register_offset == SCM_ERROR_STATUS) ||
+ (register_offset == SCM_INTERRUPT_CTRL) ||
+ (register_offset == SCM_CONFIGURATION))) {
+ pr_debug
+ ("SCC Driver: Note: Secure Memory is in BUSY state.\n");
+ } /* status is busy & register inaccessible */
+ else {
+ error_code = SCC_RET_OK;
+ }
+ }
+ /* offset within SCM */
+ return error_code;
+
+} /* check_register_accessible() */
+
+/*****************************************************************************/
+/* fn check_register_offset() */
+/*****************************************************************************/
+/*!
+ * Check that the offset is with the bounds of the SCC register set.
+ *
+ * @param[in] register_offset register offset of SMN.
+ *
+ * #SCC_RET_OK if ok, #SCC_RET_FAIL if not
+ */
+static scc_return_t check_register_offset(uint32_t register_offset)
+{
+ int return_value = SCC_RET_FAIL;
+
+ /* Is it valid word offset ? */
+ if (SCC_BYTE_OFFSET(register_offset) == 0) {
+ /* Yes. Is register within SCM? */
+ if (offset_within_scm(register_offset)) {
+ return_value = SCC_RET_OK; /* yes, all ok */
+ }
+ /* Not in SCM. Now look within the SMN */
+ else if (offset_within_smn(register_offset)) {
+ return_value = SCC_RET_OK; /* yes, all ok */
+ }
+ }
+
+ return return_value;
+}
+
+#ifdef SCC_REGISTER_DEBUG
+
+/*****************************************************************************/
+/* fn dbg_scc_read_register() */
+/*****************************************************************************/
+/*!
+ * Noisily read a 32-bit value to an SCC register.
+ * @param offset The address of the register to read.
+ *
+ * @return The register value
+ * */
+static uint32_t dbg_scc_read_register(uint32_t offset)
+{
+ uint32_t value;
+
+ value = readl(scc_base + offset);
+
+#ifndef SCC_RAM_DEBUG /* print no RAM references */
+ if ((offset < SCM_RED_MEMORY) || (offset >= scm_highest_memory_address)) {
+#endif
+ pr_debug("SCC RD: 0x%4x : 0x%08x\n", offset, value);
+#ifndef SCC_RAM_DEBUG
+ }
+#endif
+
+ return value;
+}
+
+/*****************************************************************************/
+/* fn dbg_scc_write_register() */
+/*****************************************************************************/
+/*
+ * Noisily read a 32-bit value to an SCC register.
+ * @param offset The address of the register to written.
+ *
+ * @param value The new register value
+ */
+static void dbg_scc_write_register(uint32_t offset, uint32_t value)
+{
+
+#ifndef SCC_RAM_DEBUG /* print no RAM references */
+ if ((offset < SCM_RED_MEMORY) || (offset >= scm_highest_memory_address)) {
+#endif
+ pr_debug("SCC WR: 0x%4x : 0x%08x\n", offset, value);
+#ifndef SCC_RAM_DEBUG
+ }
+#endif
+
+ (void)writel(value, scc_base + offset);
+}
+
+#endif /* SCC_REGISTER_DEBUG */
diff --git a/drivers/mxc/security/mxc_scc_internals.h b/drivers/mxc/security/mxc_scc_internals.h
new file mode 100644
index 000000000000..fddb0da9a4a5
--- /dev/null
+++ b/drivers/mxc/security/mxc_scc_internals.h
@@ -0,0 +1,498 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#ifndef __MXC_SCC_INTERNALS_H__
+#define __MXC_SCC_INTERNALS_H__
+
+/*!
+ * @file mxc_scc_internals.h
+ *
+ * @brief This is intended to be the file which contains most or all of the code or
+ * changes need to port the driver. It also includes other definitions needed
+ * by the driver.
+ *
+ * This header file should only ever be included by scc_driver.c
+ *
+ * Compile-time flags minimally needed:
+ *
+ * @li Some sort of platform flag.
+ * @li Some start-of-SCC consideration, such as SCC_BASE_ADDR
+ *
+ * Some changes which could be made when porting this driver:
+ * #SCC_SPIN_COUNT
+ *
+ * @ingroup MXCSCC
+ */
+#if 0
+#include <linux/version.h> /* Current version Linux kernel */
+#include <linux/module.h> /* Basic support for loadable modules,
+ printk */
+#include <linux/init.h> /* module_init, module_exit */
+#include <linux/kernel.h> /* General kernel system calls */
+#include <linux/sched.h> /* for interrupt.h */
+#include <linux/spinlock.h>
+#include <linux/interrupt.h> /* IRQ / interrupt definitions */
+#include <linux/io.h> /* ioremap() */
+#endif
+#include <linux/mxc_scc_driver.h>
+
+/* Get handle on certain per-platform symbols */
+#ifdef TAHITI
+#include <asm/arch/mx2.h>
+
+/*
+ * Mark the SCC as always there... as Tahiti is not officially supported by
+ * driver. Porting opportunity.
+ */
+#define SCC_ENABLED() (1)
+
+#elif defined(MXC)
+
+#include <mach/iim.h>
+#include <mach/mxc_scc.h>
+
+#ifdef SCC_FUSE
+
+/*
+ * This macro is used to determine whether the SCC is enabled/available
+ * on the platform. This macro may need to be ported.
+ */
+#define SCC_ENABLED() ((SCC_FUSE & MXC_IIMHWV1_SCC_DISABLE) == 0)
+
+#else
+
+#define SCC_ENABLED() (1)
+
+#endif
+
+#else /* neither TAHITI nor MXC */
+
+#error Do not understand target architecture
+
+#endif /* TAHITI */
+
+/* Temporarily define compile-time flags to make Doxygen happy. */
+#ifdef DOXYGEN_HACK
+/*! @addtogroup scccompileflags */
+/*! @{ */
+
+/*! @def NO_SMN_INTERRUPT
+ * The SMN interrupt is not wired to the CPU at all.
+ */
+#define NO_SMN_INTERRUPT
+
+/*!
+ * Register an interrupt handler for the SMN as well as
+ * the SCM. In some implementations, the SMN is not connected at all (see
+ * #NO_SMN_INTERRUPT), and in others, it is on the same interrupt line as the
+ * SCM. When defining this flag, the SMN interrupt should be on a separate
+ * line from the SCM interrupt.
+ */
+
+#define USE_SMN_INTERRUPT
+
+/*!
+ * Turn on generation of run-time operational, debug, and error messages
+ */
+#define SCC_DEBUG
+
+/*!
+ * Turn on generation of run-time logging of access to the SCM and SMN
+ * registers.
+ */
+#define SCC_REGISTER_DEBUG
+
+/*!
+ * Turn on generation of run-time logging of access to the SCM Red and
+ * Black memories. Will only work if #SCC_REGISTER_DEBUG is also defined.
+ */
+#define SCC_RAM_DEBUG
+
+/*!
+ * If the driver finds the SCC in HEALTH_CHECK state, go ahead and
+ * run a quick ASC to bring it to SECURE state.
+ */
+#define SCC_BRINGUP
+
+/*!
+ * Expected to come from platform header files or compile command line.
+ * This symbol must be the address of the SCC
+ */
+#define SCC_BASE
+
+/*!
+ * This must be the interrupt line number of the SCM interrupt.
+ */
+#define INT_SCM
+
+/*!
+ * if #USE_SMN_INTERRUPT is defined, this must be the interrupt line number of
+ * the SMN interrupt.
+ */
+#define INT_SMN
+
+/*!
+ * Define the number of Stored Keys which the SCC driver will make available.
+ * Value shall be from 0 to 20. Default is zero (0).
+ */
+#define SCC_KEY_SLOTS
+
+/*!
+ * Make sure that this flag is defined if compiling for a Little-Endian
+ * platform. Linux Kernel builds provide this flag.
+ */
+#define __LITTLE_ENDIAN
+
+/*!
+ * Make sure that this flag is defined if compiling for a Big-Endian platform.
+ * Linux Kernel builds provide this flag.
+ */
+#define __BIG_ENDIAN
+
+/*!
+ * Read a 32-bit register value from a 'peripheral'. Standard Linux/Unix
+ * macro.
+ *
+ * @param offset Bus address of register to be read
+ *
+ * @return The value of the register
+ */
+#define readl(offset)
+
+/*!
+ * Write a 32-bit value to a register in a 'peripheral'. Standard Linux/Unix
+ * macro.
+ *
+ * @param value The 32-bit value to store
+ * @param offset Bus address of register to be written
+ *
+ * return (none)
+ */
+#define writel(value,offset)
+
+ /*! @} *//* end group scccompileflags */
+
+#endif /* DOXYGEN_HACK */
+
+/*!
+ * Define the number of Stored Keys which the SCC driver will make available.
+ * Value shall be from 0 to 20. Default is zero (0).
+ */
+#define SCC_KEY_SLOTS 20
+
+#ifndef SCC_KEY_SLOTS
+#define SCC_KEY_SLOTS 0
+
+#else
+
+#if (SCC_KEY_SLOTS < 0) || (SCC_KEY_SLOTS > 20)
+#error Bad value for SCC_KEY_SLOTS
+#endif
+
+/*!
+ * Maximum length of key/secret value which can be stored in SCC.
+ */
+#define SCC_MAX_KEY_SIZE 32
+
+/*!
+ * This is the size, in bytes, of each key slot, and therefore the maximum size
+ * of the wrapped key.
+ */
+#define SCC_KEY_SLOT_SIZE 32
+
+/*!
+ * This is the offset into each RAM of the base of the area which is
+ * not used for Stored Keys.
+ */
+#define SCM_NON_RESERVED_OFFSET (SCC_KEY_SLOTS * SCC_KEY_SLOT_SIZE)
+
+#endif
+
+/* These come for free with Linux, but may need to be set in a port. */
+#ifndef __BIG_ENDIAN
+#ifndef __LITTLE_ENDIAN
+#error One of __LITTLE_ENDIAN or __BIG_ENDIAN must be #defined
+#endif
+#else
+#ifdef __LITTLE_ENDIAN
+#error Exactly one of __LITTLE_ENDIAN or __BIG_ENDIAN must be #defined
+#endif
+#endif
+
+#ifndef SCC_CALLBACK_SIZE
+/*! The number of function pointers which can be stored in #scc_callbacks.
+ * Defaults to 4, can be overridden with compile-line argument.
+ */
+#define SCC_CALLBACK_SIZE 4
+#endif
+
+/*! Initial CRC value for CCITT-CRC calculation. */
+#define CRC_CCITT_START 0xFFFF
+
+#ifdef TAHITI
+
+/*!
+ * The SCC_BASE has to be SMN_BASE_ADDR on TAHITI, as the banks of
+ * registers are swapped in place.
+ */
+#define SCC_BASE SMN_BASE_ADDR
+
+/*! The interrupt number for the SCC (SCM only!) on Tahiti */
+#define INT_SCC_SCM 62
+
+/*! Tahiti does not have the SMN interrupt wired to the CPU. */
+#define NO_SMN_INTERRUPT
+
+#endif /* TAHITI */
+
+/*! Number of times to spin between polling of SCC while waiting for cipher
+ * or zeroizing function to complete. See also #SCC_CIPHER_MAX_POLL_COUNT. */
+#define SCC_SPIN_COUNT 1000
+
+/*! Number of times to polling SCC while waiting for cipher
+ * or zeroizing function to complete. See also #SCC_SPIN_COUNT. */
+#define SCC_CIPHER_MAX_POLL_COUNT 100
+
+/*!
+ * @def SCC_READ_REGISTER
+ * Read a 32-bit value from an SCC register. Macro which depends upon
+ * #scc_base. Linux readl()/writel() macros operate on 32-bit quantities, as
+ * do SCC register reads/writes.
+ *
+ * @param offset Register offset within SCC.
+ *
+ * @return The value from the SCC's register.
+ */
+#ifndef SCC_REGISTER_DEBUG
+#define SCC_READ_REGISTER(offset) __raw_readl(scc_base+(offset))
+#else
+#define SCC_READ_REGISTER(offset) dbg_scc_read_register(offset)
+#endif
+
+/*!
+ * Write a 32-bit value to an SCC register. Macro depends upon #scc_base.
+ * Linux readl()/writel() macros operate on 32-bit quantities, as do SCC
+ * register reads/writes.
+ *
+ * @param offset Register offset within SCC.
+ * @param value 32-bit value to store into the register
+ *
+ * @return (void)
+ */
+#ifndef SCC_REGISTER_DEBUG
+#define SCC_WRITE_REGISTER(offset,value) (void)__raw_writel(value, scc_base+(offset))
+#else
+#define SCC_WRITE_REGISTER(offset,value) dbg_scc_write_register(offset, value)
+#endif
+
+/*!
+ * Calculates the byte offset into a word
+ * @param bp The byte (char*) pointer
+ * @return The offset (0, 1, 2, or 3)
+ */
+#define SCC_BYTE_OFFSET(bp) ((uint32_t)(bp) % sizeof(uint32_t))
+
+/*!
+ * Converts (by rounding down) a byte pointer into a word pointer
+ * @param bp The byte (char*) pointer
+ * @return The word (uint32_t) as though it were an aligned (uint32_t*)
+ */
+#define SCC_WORD_PTR(bp) (((uint32_t)(bp)) & ~(sizeof(uint32_t)-1))
+
+/*!
+ * Determine number of bytes in an SCC block
+ *
+ * @return Bytes / block
+ */
+#define SCC_BLOCK_SIZE_BYTES() scc_configuration.block_size_bytes
+
+/*!
+ * Maximum number of additional bytes which may be added in CRC+padding mode.
+ */
+#define PADDING_BUFFER_MAX_BYTES (CRC_SIZE_BYTES + sizeof(scc_block_padding))
+
+/*!
+ * Shorthand (clearer, anyway) for number of bytes in a CRC.
+ */
+#define CRC_SIZE_BYTES (sizeof(crc_t))
+
+/*!
+ * The polynomial used in CCITT-CRC calculation
+ */
+#define CRC_POLYNOMIAL 0x1021
+
+/*!
+ * Calculate CRC on one byte of data
+ *
+ * @param[in,out] running_crc A value of type crc_t where CRC is kept. This
+ * must be an rvalue and an lvalue.
+ * @param[in] byte_value The byte (uint8_t, char) to be put in the CRC
+ *
+ * @return none
+ */
+#define CALC_CRC(byte_value,running_crc) { \
+ uint8_t data; \
+ data = (0xff&(byte_value)) ^ (running_crc >> 8); \
+ running_crc = scc_crc_lookup_table[data] ^ (running_crc << 8); \
+}
+
+/*! Value of 'beginning of padding' marker in driver-provided padding */
+#define SCC_DRIVER_PAD_CHAR 0x80
+
+/*! Name of the driver. Used (on Linux, anyway) when registering interrupts */
+#define SCC_DRIVER_NAME "scc"
+
+/* Port -- these symbols are defined in Linux 2.6 and later. They are defined
+ * here for backwards compatibility because this started life as a 2.4
+ * driver, and as a guide to portation to other platforms.
+ */
+
+#if !defined(LINUX_VERSION_CODE) || LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+
+#define irqreturn_t void /* Return type of an interrupt handler */
+
+#define IRQ_HANDLED /* Would be '1' for handled -- as in return IRQ_HANDLED; */
+
+#define IRQ_NONE /* would be '0' for not handled -- as in return IRQ_NONE; */
+
+#define IRQ_RETVAL(x) /* Return x==0 (not handled) or non-zero (handled) */
+
+#endif /* LINUX earlier than 2.5 */
+
+/* These are nice to have around */
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+/*! Provide a typedef for the CRC which can be used in encrypt/decrypt */
+typedef uint16_t crc_t;
+
+/*! Gives high-level view of state of the SCC */
+enum scc_status {
+ SCC_STATUS_INITIAL, /*!< State of driver before ever checking */
+ SCC_STATUS_CHECKING, /*!< Transient state while driver loading */
+ SCC_STATUS_UNIMPLEMENTED, /*!< SCC is non-existent or unuseable */
+ SCC_STATUS_OK, /*!< SCC is in Secure or Default state */
+ SCC_STATUS_FAILED /*!< In Failed state */
+};
+
+/*!
+ * Information about a key slot.
+ */
+struct scc_key_slot {
+ uint64_t owner_id; /*!< Access control value. */
+ uint32_t length; /*!< Length of value in slot. */
+ uint32_t offset; /*!< Offset of value from start of each RAM. */
+ uint32_t status; /*!< 0 = unassigned, 1 = assigned. */
+};
+
+/* Forward-declare a number routines which are not part of user api */
+static int scc_init(void);
+static void scc_cleanup(void);
+
+/* Forward defines of internal functions */
+OS_DEV_ISR(scc_irq);
+/*! Perform callbacks registered by #scc_monitor_security_failure().
+ *
+ * Make sure callbacks only happen once... Since there may be some reason why
+ * the interrupt isn't generated, this routine could be called from base(task)
+ * level.
+ *
+ * One at a time, go through #scc_callbacks[] and call any non-null pointers.
+ */
+static void scc_perform_callbacks(void);
+static uint32_t copy_to_scc(const uint8_t * from, uint32_t to,
+ unsigned long count_bytes, uint16_t * crc);
+static uint32_t copy_from_scc(const uint32_t from, uint8_t * to,
+ unsigned long count_bytes, uint16_t * crc);
+static scc_return_t scc_strip_padding(uint8_t * from,
+ unsigned *count_bytes_stripped);
+static uint32_t scc_update_state(void);
+static void scc_init_ccitt_crc(void);
+static uint32_t scc_grab_config_values(void);
+static int setup_interrupt_handling(void);
+/*!
+ * Perform an encryption on the input. If @c verify_crc is true, a CRC must be
+ * calculated on the plaintext, and appended, with padding, before computing
+ * the ciphertext.
+ *
+ * @param[in] count_in_bytes Count of bytes of plaintext
+ * @param[in] data_in Pointer to the plaintext
+ * @param[in] scm_control Bit values for the SCM_CONTROL register
+ * @param[in,out] data_out Pointer for storing ciphertext
+ * @param[in] add_crc Flag for computing CRC - 0 no, else yes
+ * @param[in,out] count_out_bytes Number of bytes available at @c data_out
+ */
+static scc_return_t scc_encrypt(uint32_t count_in_bytes,
+ const uint8_t * data_in,
+ uint32_t scm_control, uint8_t * data_out,
+ int add_crc, unsigned long *count_out_bytes);
+
+/*!
+ * Perform a decryption on the input. If @c verify_crc is true, the last block
+ * (maybe the two last blocks) is special - it should contain a CRC and
+ * padding. These must be stripped and verified.
+ *
+ * @param[in] count_in_bytes Count of bytes of ciphertext
+ * @param[in] data_in Pointer to the ciphertext
+ * @param[in] scm_control Bit values for the SCM_CONTROL register
+ * @param[in,out] data_out Pointer for storing plaintext
+ * @param[in] verify_crc Flag for running CRC - 0 no, else yes
+ * @param[in,out] count_out_bytes Number of bytes available at @c data_out
+
+ */
+static scc_return_t scc_decrypt(uint32_t count_in_bytes,
+ const uint8_t * data_in,
+ uint32_t scm_control, uint8_t * data_out,
+ int verify_crc, unsigned long *count_out_bytes);
+
+static void scc_wait_completion(void);
+static int is_cipher_done(void);
+static scc_return_t check_register_accessible(uint32_t offset,
+ uint32_t smn_status,
+ uint32_t scm_status);
+static scc_return_t check_register_offset(uint32_t offset);
+
+#ifdef SCC_REGISTER_DEBUG
+static uint32_t dbg_scc_read_register(uint32_t offset);
+static void dbg_scc_write_register(uint32_t offset, uint32_t value);
+#endif
+
+/* For Linux kernel, export the API functions to other kernel modules */
+EXPORT_SYMBOL(scc_get_configuration);
+EXPORT_SYMBOL(scc_zeroize_memories);
+EXPORT_SYMBOL(scc_crypt);
+EXPORT_SYMBOL(scc_set_sw_alarm);
+EXPORT_SYMBOL(scc_monitor_security_failure);
+EXPORT_SYMBOL(scc_stop_monitoring_security_failure);
+EXPORT_SYMBOL(scc_read_register);
+EXPORT_SYMBOL(scc_write_register);
+EXPORT_SYMBOL(scc_alloc_slot);
+EXPORT_SYMBOL(scc_dealloc_slot);
+EXPORT_SYMBOL(scc_load_slot);
+EXPORT_SYMBOL(scc_encrypt_slot);
+EXPORT_SYMBOL(scc_decrypt_slot);
+EXPORT_SYMBOL(scc_get_slot_info);
+
+/* Tell Linux where to invoke driver at boot/module load time */
+module_init(scc_init);
+/* Tell Linux where to invoke driver on module unload */
+module_exit(scc_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Device Driver for SCC (SMN/SCM)");
+
+#endif /* __MXC_SCC_INTERNALS_H__ */
diff --git a/drivers/mxc/security/rng/Makefile b/drivers/mxc/security/rng/Makefile
new file mode 100644
index 000000000000..7d3332e2e675
--- /dev/null
+++ b/drivers/mxc/security/rng/Makefile
@@ -0,0 +1,35 @@
+# Makefile for the Linux RNG Driver
+#
+# This makefile works within a kernel driver tree
+
+ # Makefile for rng_driver
+
+
+# Possible configurable paramters
+CFG_RNG += -DRNGA_MAX_REQUEST_SIZE=32
+
+#DBG_RNGA = -DRNGA_DEBUG
+#DBG_RNGA += -DRNGA_REGISTER_DEBUG
+#DBG_RNGA += -DRNGA_ENTROPY_DEBUG
+
+EXTRA_CFLAGS = -DLINUX_KERNEL $(CFG_RNG) $(DBG_RNG)
+
+
+ifeq ($(CONFIG_MXC_RNG_TEST_DRIVER),y)
+EXTRA_CFLAGS += -DRNG_REGISTER_PEEK_POKE
+endif
+ifeq ($(CONFIG_RNG_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+
+
+EXTRA_CFLAGS += -Idrivers/mxc/security/rng/include -Idrivers/mxc/security/sahara2/include
+
+obj-$(CONFIG_MXC_SECURITY_RNG) += shw.o
+#shw-objs := shw_driver.o shw_memory_mapper.o ../sahara2/fsl_shw_keystore.o
+shw-objs := shw_driver.o shw_memory_mapper.o ../sahara2/fsl_shw_keystore.o \
+ fsl_shw_sym.o fsl_shw_wrap.o shw_dryice.o des_key.o \
+ shw_hash.o shw_hmac.o
+
+obj-$(CONFIG_MXC_SECURITY_RNG) += rng.o
+rng-objs := rng_driver.o
diff --git a/drivers/mxc/security/rng/des_key.c b/drivers/mxc/security/rng/des_key.c
new file mode 100644
index 000000000000..14ac2b3cdd5e
--- /dev/null
+++ b/drivers/mxc/security/rng/des_key.c
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+
+/*!
+ * @file des_key.c
+ *
+ * This file implements the function #fsl_shw_permute1_bytes().
+ *
+ * The code was lifted from crypto++ v5.5.2, which is public domain code. The
+ * code to handle words instead of bytes was extensively modified from the byte
+ * version and then converted to handle one to three keys at once.
+ *
+ */
+
+#include "shw_driver.h"
+#ifdef DIAG_SECURITY_FUNC
+#include "apihelp.h"
+#endif
+
+#ifndef __KERNEL__
+#include <asm/types.h>
+#include <linux/byteorder/little_endian.h> /* or whichever is proper for target arch */
+#endif
+
+#ifdef DEBUG
+#undef DEBUG /* TEMPORARY */
+#endif
+
+#if defined(DEBUG) || defined(SELF_TEST)
+static void DUMP_BYTES(const char *label, const uint8_t * data, int len)
+{
+ int i;
+
+ printf("%s: ", label);
+ for (i = 0; i < len; i++) {
+ printf("%02X", data[i]);
+ if ((i % 8 == 0) && (i != 0)) {
+ printf("_"); /* key separator */
+ }
+ }
+ printf("\n");
+}
+
+static void DUMP_WORDS(const char *label, const uint32_t * data, int len)
+{
+ int i, j;
+
+ printf("%s: ", label);
+ /* Dump the words in reverse order, so that they are intelligible */
+ for (i = len - 1; i >= 0; i--) {
+ for (j = 3; j >= 0; j--) {
+ uint32_t word = data[i];
+ printf("%02X", (word >> ((j * 8)) & 0xff));
+ if ((i != 0) && ((((i) * 4 + 5 + j) % 7) == 5))
+ printf("_"); /* key separator */
+ }
+ printf("|"); /* word separator */
+ }
+ printf("\n");
+}
+#else
+#define DUMP_BYTES(label, data,len)
+#define DUMP_WORDS(label, data,len)
+#endif
+
+/*!
+ * permuted choice table (key)
+ *
+ * Note that this table has had one subtracted from each element so that the
+ * code doesn't have to do it.
+ */
+static const uint8_t pc1[] = {
+ 56, 48, 40, 32, 24, 16, 8,
+ 0, 57, 49, 41, 33, 25, 17,
+ 9, 1, 58, 50, 42, 34, 26,
+ 18, 10, 2, 59, 51, 43, 35,
+ 62, 54, 46, 38, 30, 22, 14,
+ 6, 61, 53, 45, 37, 29, 21,
+ 13, 5, 60, 52, 44, 36, 28,
+ 20, 12, 4, 27, 19, 11, 3,
+};
+
+/*! bit 0 is left-most in byte */
+static const int bytebit[] = {
+ 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
+};
+
+/*!
+ * Convert a 3-key 3DES key into the first-permutation 168-bit version.
+ *
+ * This is the format of the input key:
+ *
+ * @verbatim
+ BIT: |191 128|127 64|63 0|
+ BYTE: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
+ KEY: | 0 | 1 | 2 |
+ @endverbatim
+ *
+ * This is the format of the output key:
+ *
+ * @verbatim
+ BIT: |167 112|111 56|55 0|
+ BYTE: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
+ KEY: | 1 | 2 | 3 |
+ @endverbatim
+ *
+ * @param[in] key bytes of 3DES key
+ * @param[out] permuted_key 21 bytes of permuted key
+ * @param[in] key_count How many DES keys (2 or 3)
+ */
+void fsl_shw_permute1_bytes(const uint8_t * key, uint8_t * permuted_key,
+ int key_count)
+{
+ int i;
+ int j;
+ int l;
+ int m;
+
+ DUMP_BYTES("Input key", key, 8 * key_count);
+
+ /* For each individual sub-key */
+ for (i = 0; i < 3; i++) {
+ DUMP_BYTES("(key)", key, 8);
+ memset(permuted_key, 0, 7);
+ /* For each bit of key */
+ for (j = 0; j < 56; j++) { /* convert pc1 to bits of key */
+ l = pc1[j]; /* integer bit location */
+ m = l & 07; /* find bit */
+ permuted_key[j >> 3] |= (((key[l >> 3] & /* find which key byte l is in */
+ bytebit[m]) /* and which bit of that byte */
+ ? 0x80 : 0) >> (j % 8)); /* and store 1-bit result */
+ }
+ switch (i) {
+ case 0:
+ if (key_count != 1)
+ key += 8; /* move on to second key */
+ break;
+ case 1:
+ if (key_count == 2)
+ key -= 8; /* go back to first key */
+ else if (key_count == 3)
+ key += 8; /* move on to third key */
+ break;
+ default:
+ break;
+ }
+ permuted_key += 7;
+ }
+ DUMP_BYTES("Output key (bytes)", permuted_key - 21, 21);
+}
+
+#ifdef SELF_TEST
+const uint8_t key1_in[] = {
+ /* FE01FE01FE01FE01_01FE01FE01FE01FE_FEFE0101FEFE0101 */
+ 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01,
+ 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE,
+ 0xFE, 0xFE, 0x01, 0x01, 0xFE, 0xFE, 0x01, 0x01
+};
+
+const uint32_t key1_word_in[] = {
+ 0xFE01FE01, 0xFE01FE01,
+ 0x01FE01FE, 0x01FE01FE,
+ 0xFEFE0101, 0xFEFE0101
+};
+
+uint8_t exp_key1_out[] = {
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33
+};
+
+uint32_t exp_word_key1_out[] = {
+ 0x33333333, 0xAA333333, 0xAAAAAAAA, 0x5555AAAA,
+ 0x55555555, 0x00000055,
+};
+
+const uint8_t key2_in[] = {
+ 0xEF, 0x10, 0xBB, 0xA4, 0x23, 0x49, 0x42, 0x58,
+ 0x01, 0x28, 0x01, 0x4A, 0x10, 0xE4, 0x03, 0x59,
+ 0xFE, 0x84, 0x30, 0x29, 0x8E, 0xF1, 0x10, 0x5A
+};
+
+const uint32_t key2_word_in[] = {
+ 0xEF10BBA4, 0x23494258,
+ 0x0128014A, 0x10E40359,
+ 0xFE843029, 0x8EF1105A
+};
+
+uint8_t exp_key2_out[] = {
+ 0x0D, 0xE1, 0x1D, 0x85, 0x50, 0x9A, 0x56, 0x20,
+ 0xA8, 0x22, 0x94, 0x82, 0x08, 0xA0, 0x33, 0xA1,
+ 0x2D, 0xE9, 0x11, 0x39, 0x95
+};
+
+uint32_t exp_word_key2_out[] = {
+ 0xE9113995, 0xA033A12D, 0x22948208, 0x9A5620A8,
+ 0xE11D8550, 0x0000000D
+};
+
+const uint8_t key3_in[] = {
+ 0x3F, 0xE9, 0x49, 0x4B, 0x67, 0x57, 0x07, 0x3C,
+ 0x89, 0x77, 0x73, 0x0C, 0xA0, 0x05, 0x41, 0x69,
+ 0xB3, 0x7C, 0x98, 0xD8, 0xC9, 0x35, 0x57, 0x19
+};
+
+const uint32_t key3_word_in[] = {
+ 0xEF10BBA4, 0x23494258,
+ 0x0128014A, 0x10E40359,
+ 0xFE843029, 0x8EF1105A
+};
+
+uint8_t exp_key3_out[] = {
+ 0x02, 0x3E, 0x93, 0xA7, 0x9F, 0x18, 0xF1, 0x11,
+ 0xC6, 0x96, 0x00, 0x62, 0xA8, 0x96, 0x02, 0x3E,
+ 0x93, 0xA7, 0x9F, 0x18, 0xF1
+};
+
+uint32_t exp_word_key3_out[] = {
+ 0xE9113995, 0xA033A12D, 0x22948208, 0x9A5620A8,
+ 0xE11D8550, 0x0000000D
+};
+
+const uint8_t key4_in[] = {
+ 0x3F, 0xE9, 0x49, 0x4B, 0x67, 0x57, 0x07, 0x3C,
+ 0x89, 0x77, 0x73, 0x0C, 0xA0, 0x05, 0x41, 0x69,
+};
+
+const uint32_t key4_word_in[] = {
+ 0xEF10BBA4, 0x23494258,
+ 0x0128014A, 0x10E40359,
+ 0xFE843029, 0x8EF1105A
+};
+
+const uint8_t key5_in[] = {
+ 0x3F, 0xE9, 0x49, 0x4B, 0x67, 0x57, 0x07, 0x3C,
+ 0x89, 0x77, 0x73, 0x0C, 0xA0, 0x05, 0x41, 0x69,
+ 0x3F, 0xE9, 0x49, 0x4B, 0x67, 0x57, 0x07, 0x3C,
+};
+
+uint8_t exp_key4_out[] = {
+ 0x0D, 0xE1, 0x1D, 0x85, 0x50, 0x9A, 0x56, 0x20,
+ 0xA8, 0x22, 0x94, 0x82, 0x08, 0xA0, 0x33, 0xA1,
+ 0x2D, 0xE9, 0x11, 0x39, 0x95
+};
+
+uint32_t exp_word_key4_out[] = {
+ 0xE9113995, 0xA033A12D, 0x22948208, 0x9A5620A8,
+ 0xE11D8550, 0x0000000D
+};
+
+const uint8_t key6_in[] = {
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
+};
+
+uint8_t exp_key6_out[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+uint32_t exp_word_key6_out[] = {
+ 0x00000000, 0x0000000, 0x0000000, 0x00000000,
+ 0x00000000, 0x0000000
+};
+
+const uint8_t key7_in[] = {
+ /* 01FE01FE01FE01FE_FE01FE01FE01FE01_0101FEFE0101FEFE */
+ /* 0101FEFE0101FEFE_FE01FE01FE01FE01_01FE01FE01FE01FE */
+ 0x01, 0x01, 0xFE, 0xFE, 0x01, 0x01, 0xFE, 0xFE,
+ 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01,
+ 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE,
+};
+
+uint8_t exp_key7_out[] = {
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x55,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+};
+
+uint32_t exp_word_key7_out[] = {
+ 0xcccccccc, 0x55cccccc, 0x55555555, 0xaaaa5555,
+ 0xaaaaaaaa, 0x000000aa
+};
+
+int run_test(const uint8_t * key_in,
+ const int key_count,
+ const uint32_t * key_word_in,
+ const uint8_t * exp_bytes_key_out,
+ const uint32_t * exp_word_key_out)
+{
+ uint8_t key_out[22];
+ uint32_t word_key_out[6];
+ int failed = 0;
+
+ memset(key_out, 0x42, 22);
+ fsl_shw_permute1_bytes(key_in, key_out, key_count);
+ if (memcmp(key_out, exp_bytes_key_out, 21) != 0) {
+ printf("bytes_to_bytes: ERROR: \n");
+ DUMP_BYTES("key_in", key_in, 8 * key_count);
+ DUMP_BYTES("key_out", key_out, 21);
+ DUMP_BYTES("exp_out", exp_bytes_key_out, 21);
+ failed |= 1;
+ } else if (key_out[21] != 0x42) {
+ printf("bytes_to_bytes: ERROR: Buffer overflow 0x%02x\n",
+ (int)key_out[21]);
+ } else {
+ printf("bytes_to_bytes: OK\n");
+ }
+#if 0
+ memset(word_key_out, 0x42, 21);
+ fsl_shw_permute1_bytes_to_words(key_in, word_key_out, key_count);
+ if (memcmp(word_key_out, exp_word_key_out, 21) != 0) {
+ printf("bytes_to_words: ERROR: \n");
+ DUMP_BYTES("key_in", key_in, 8 * key_count);
+ DUMP_WORDS("key_out", word_key_out, 6);
+ DUMP_WORDS("exp_out", exp_word_key_out, 6);
+ failed |= 1;
+ } else {
+ printf("bytes_to_words: OK\n");
+ }
+
+ if (key_word_in != NULL) {
+ memset(word_key_out, 0x42, 21);
+ fsl_shw_permute1_words_to_words(key_word_in, word_key_out);
+ if (memcmp(word_key_out, exp_word_key_out, 21) != 0) {
+ printf("words_to_words: ERROR: \n");
+ DUMP_BYTES("key_in", key_in, 24);
+ DUMP_WORDS("key_out", word_key_out, 6);
+ DUMP_WORDS("exp_out", exp_word_key_out, 6);
+ failed |= 1;
+ } else {
+ printf("words_to_words: OK\n");
+ }
+ }
+#endif
+
+ return failed;
+} /* end fn run_test */
+
+int main()
+{
+ int failed = 0;
+
+ printf("key1\n");
+ failed |=
+ run_test(key1_in, 3, key1_word_in, exp_key1_out, exp_word_key1_out);
+ printf("\nkey2\n");
+ failed |=
+ run_test(key2_in, 3, key2_word_in, exp_key2_out, exp_word_key2_out);
+ printf("\nkey3\n");
+ failed |= run_test(key3_in, 3, NULL, exp_key3_out, exp_word_key3_out);
+ printf("\nkey4\n");
+ failed |= run_test(key4_in, 2, NULL, exp_key4_out, exp_word_key4_out);
+ printf("\nkey5\n");
+ failed |= run_test(key5_in, 3, NULL, exp_key4_out, exp_word_key4_out);
+ printf("\nkey6 - 3\n");
+ failed |= run_test(key6_in, 3, NULL, exp_key6_out, exp_word_key6_out);
+ printf("\nkey6 - 2\n");
+ failed |= run_test(key6_in, 2, NULL, exp_key6_out, exp_word_key6_out);
+ printf("\nkey6 - 1\n");
+ failed |= run_test(key6_in, 1, NULL, exp_key6_out, exp_word_key6_out);
+ printf("\nkey7\n");
+ failed |= run_test(key7_in, 3, NULL, exp_key7_out, exp_word_key7_out);
+ printf("\n");
+
+ if (failed != 0) {
+ printf("TEST FAILED\n");
+ }
+ return failed;
+}
+
+#endif /* SELF_TEST */
diff --git a/drivers/mxc/security/rng/fsl_shw_hash.c b/drivers/mxc/security/rng/fsl_shw_hash.c
new file mode 100644
index 000000000000..56b2dcc34035
--- /dev/null
+++ b/drivers/mxc/security/rng/fsl_shw_hash.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+
+/*!
+ * @file fsl_shw_hash.c
+ *
+ * This file implements Cryptographic Hashing functions of the FSL SHW API
+ * for Sahara. This does not include HMAC.
+ */
+
+#include "shw_driver.h"
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-005 */
+/*!
+ * Hash a stream of data with a cryptographic hash algorithm.
+ *
+ * The flags in the @a hash_ctx control the operation of this function.
+ *
+ * Hashing functions work on 64 octets of message at a time. Therefore, when
+ * any partial hashing of a long message is performed, the message @a length of
+ * each segment must be a multiple of 64. When ready to
+ * #FSL_HASH_FLAGS_FINALIZE the hash, the @a length may be any value.
+ *
+ * With the #FSL_HASH_FLAGS_INIT and #FSL_HASH_FLAGS_FINALIZE flags on, a
+ * one-shot complete hash, including padding, will be performed. The @a length
+ * may be any value.
+ *
+ * The first octets of a data stream can be hashed by setting the
+ * #FSL_HASH_FLAGS_INIT and #FSL_HASH_FLAGS_SAVE flags. The @a length must be
+ * a multiple of 64.
+ *
+ * The flag #FSL_HASH_FLAGS_LOAD is used to load a context previously saved by
+ * #FSL_HASH_FLAGS_SAVE. The two in combination will allow a (multiple-of-64
+ * octets) 'middle sequence' of the data stream to be hashed with the
+ * beginning. The @a length must again be a multiple of 64.
+ *
+ * Since the flag #FSL_HASH_FLAGS_LOAD is used to load a context previously
+ * saved by #FSL_HASH_FLAGS_SAVE, the #FSL_HASH_FLAGS_LOAD and
+ * #FSL_HASH_FLAGS_FINALIZE flags, used together, can be used to finish the
+ * stream. The @a length may be any value.
+ *
+ * If the user program wants to do the padding for the hash, it can leave off
+ * the #FSL_HASH_FLAGS_FINALIZE flag. The @a length must then be a multiple of
+ * 64 octets.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] hash_ctx Hashing algorithm and state of the cipher.
+ * @param msg Pointer to the data to be hashed.
+ * @param length Length, in octets, of the @a msg.
+ * @param[out] result If not null, pointer to where to store the hash
+ * digest.
+ * @param result_len Number of octets to store in @a result.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_hash(fsl_shw_uco_t * user_ctx,
+ fsl_shw_hco_t * hash_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ /* Unused */
+ (void)user_ctx;
+ (void)hash_ctx;
+ (void)msg;
+ (void)length;
+ (void)result;
+ (void)result_len;
+
+ return ret;
+}
diff --git a/drivers/mxc/security/rng/fsl_shw_hmac.c b/drivers/mxc/security/rng/fsl_shw_hmac.c
new file mode 100644
index 000000000000..23a2fb3d3292
--- /dev/null
+++ b/drivers/mxc/security/rng/fsl_shw_hmac.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+
+/*!
+ * @file fsl_shw_hmac.c
+ *
+ * This file implements Hashed Message Authentication Code functions of the FSL
+ * SHW API.
+ */
+
+#include "shw_driver.h"
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HMAC-001 */
+/*!
+ * Get the precompute information
+ *
+ *
+ * @param user_ctx
+ * @param key_info
+ * @param hmac_ctx
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_hmac_precompute(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx)
+{
+ fsl_shw_return_t status = FSL_RETURN_ERROR_S;
+
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)hmac_ctx;
+
+ return status;
+}
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HMAC-002 */
+/*!
+ * Get the hmac
+ *
+ *
+ * @param user_ctx Info for acquiring memory
+ * @param key_info
+ * @param hmac_ctx
+ * @param msg
+ * @param length
+ * @param result
+ * @param result_len
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_hmac(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len)
+{
+ fsl_shw_return_t status = FSL_RETURN_ERROR_S;
+
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)hmac_ctx;
+ (void)msg;
+ (void)length;
+ (void)result;
+ (void)result_len;
+
+ return status;
+}
diff --git a/drivers/mxc/security/rng/fsl_shw_rand.c b/drivers/mxc/security/rng/fsl_shw_rand.c
new file mode 100644
index 000000000000..7ba49d8d470f
--- /dev/null
+++ b/drivers/mxc/security/rng/fsl_shw_rand.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+
+/*!
+ * @file fsl_shw_rand.c
+ *
+ * This file implements Random Number Generation functions of the FSL SHW API
+ * in USER MODE for talking to a standalone RNGA/RNGC device driver.
+ *
+ * It contains the fsl_shw_get_random() and fsl_shw_add_entropy() functions.
+ *
+ * These routines will build a request block and pass it to the SHW driver.
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+
+#ifdef FSL_DEBUG
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#endif /* FSL_DEBUG */
+
+#include "shw_driver.h"
+
+extern fsl_shw_return_t validate_uco(fsl_shw_uco_t * uco);
+
+#if defined(FSL_HAVE_RNGA) || defined(FSL_HAVE_RNGB) || defined(FSL_HAVE_RNGC)
+
+/* REQ-S2LRD-PINTFC-API-BASIC-RNG-002 */
+fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ /* perform a sanity check / update uco */
+ ret = validate_uco(user_ctx);
+ if (ret == FSL_RETURN_OK_S) {
+ struct get_random_req *req = malloc(sizeof(*req));
+
+ if (req == NULL) {
+ ret = FSL_RETURN_NO_RESOURCE_S;
+ } else {
+
+ init_req(&req->hdr, user_ctx);
+ req->size = length;
+ req->random = data;
+
+ ret =
+ send_req(SHW_USER_REQ_GET_RANDOM, &req->hdr,
+ user_ctx);
+ }
+ }
+
+ return ret;
+}
+
+fsl_shw_return_t fsl_shw_add_entropy(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ /* perform a sanity check on the uco */
+ ret = validate_uco(user_ctx);
+ if (ret == FSL_RETURN_OK_S) {
+ struct add_entropy_req *req = malloc(sizeof(*req));
+
+ if (req == NULL) {
+ ret = FSL_RETURN_NO_RESOURCE_S;
+ } else {
+ init_req(&req->hdr, user_ctx);
+ req->size = length;
+ req->entropy = data;
+
+ ret =
+ send_req(SHW_USER_REQ_ADD_ENTROPY, &req->hdr,
+ user_ctx);
+ }
+ }
+
+ return ret;
+}
+
+#else /* no H/W RNG block */
+
+fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data)
+{
+
+ (void)user_ctx;
+ (void)length;
+ (void)data;
+
+ return FSL_RETURN_ERROR_S;
+}
+
+fsl_shw_return_t fsl_shw_add_entropy(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data)
+{
+
+ (void)user_ctx;
+ (void)length;
+ (void)data;
+
+ return FSL_RETURN_ERROR_S;
+}
+#endif
diff --git a/drivers/mxc/security/rng/fsl_shw_sym.c b/drivers/mxc/security/rng/fsl_shw_sym.c
new file mode 100644
index 000000000000..bcd8a0efcced
--- /dev/null
+++ b/drivers/mxc/security/rng/fsl_shw_sym.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+
+/*!
+ * @file fsl_shw_sym.c
+ *
+ * This file implements the Symmetric Cipher functions of the FSL SHW API. Its
+ * features are limited to what can be done with the combination of SCC and
+ * DryIce.
+ */
+#include "fsl_platform.h"
+#include "shw_driver.h"
+
+#if defined(__KERNEL__) && defined(FSL_HAVE_DRYICE)
+
+#include "../dryice.h"
+#include <linux/mxc_scc_driver.h>
+#ifdef DIAG_SECURITY_FUNC
+#include "apihelp.h"
+#endif
+
+#include <diagnostic.h>
+
+#define SYM_DECRYPT 0
+#define SYM_ENCRYPT 1
+
+extern fsl_shw_return_t shw_convert_pf_key(fsl_shw_pf_key_t shw_pf_key,
+ di_key_t * di_keyp);
+
+/*! 'Initial' IV for presence of FSL_SYM_CTX_LOAD flag */
+static uint8_t zeros[8] = {
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*!
+ * Common function for encryption and decryption
+ *
+ * This is for a device with DryIce.
+ *
+ * A key must either refer to a 'pure' HW key, or, if PRG or PRG_IIM,
+ * established, then that key will be programmed. Then, the HW_key in the
+ * object will be selected. After this setup, the ciphering will be performed
+ * by calling the SCC driver..
+ *
+ * The function 'releases' the reservations before it completes.
+ */
+fsl_shw_return_t do_symmetric(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ int encrypt,
+ uint32_t length,
+ const uint8_t * in, uint8_t * out)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+ int key_selected = 0;
+ uint8_t *iv = NULL;
+ unsigned long count_out = length;
+ di_key_t di_key = DI_KEY_PK; /* default for user key */
+ di_key_t di_key_orig; /* currently selected key */
+ di_key_t selected_key = -1;
+ di_return_t di_code;
+ scc_return_t scc_code;
+
+ /* For now, only blocking mode calls are supported */
+ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ /* No software keys allowed */
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ }
+
+ /* The only algorithm the SCC supports */
+ if (key_info->algorithm != FSL_KEY_ALG_TDES) {
+ ret = FSL_RETURN_BAD_ALGORITHM_S;
+ goto out;
+ }
+
+ /* Validate key length */
+ if ((key_info->key_length != 16)
+ && (key_info->key_length != 21)
+ && (key_info->key_length != 24)) {
+ ret = FSL_RETURN_BAD_KEY_LENGTH_S;
+ goto out;
+ }
+
+ /* Validate data is multiple of DES/TDES block */
+ if ((length & 7) != 0) {
+ ret = FSL_RETURN_BAD_DATA_LENGTH_S;
+ goto out;
+ }
+
+ /* Do some setup according to where the key lives */
+ if (key_info->flags & FSL_SKO_KEY_ESTABLISHED) {
+ if ((key_info->pf_key != FSL_SHW_PF_KEY_PRG)
+ && (key_info->pf_key != FSL_SHW_PF_KEY_IIM_PRG)) {
+ ret = FSL_RETURN_ERROR_S;
+ }
+ } else if (key_info->flags & FSL_SKO_KEY_PRESENT) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ } else if (key_info->flags & FSL_SKO_KEY_SELECT_PF_KEY) {
+ /*
+ * No key present or established, just refer to HW
+ * as programmed.
+ */
+ } else {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ /* Now make proper selection */
+ ret = shw_convert_pf_key(key_info->pf_key, &di_key);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Determine the current DI key selection */
+ di_code = dryice_check_key(&di_key_orig);
+ if (di_code != DI_SUCCESS) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Could not save current DI key state: %s\n",
+ di_error_string(di_code));
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ /* If the requested DI key is already selected, don't re-select it. */
+ if (di_key != di_key_orig) {
+ di_code = dryice_select_key(di_key, 0);
+ if (di_code != DI_SUCCESS) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Error from select_key: %s\n",
+ di_error_string(di_code));
+#endif
+ ret = FSL_RETURN_INTERNAL_ERROR_S;
+ goto out;
+ }
+ }
+ key_selected = 1;
+
+ /* Verify that we are using the key we want */
+ di_code = dryice_check_key(&selected_key);
+ if (di_code != DI_SUCCESS) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Error from check_key: %s\n",
+ di_error_string(di_code));
+#endif
+ ret = FSL_RETURN_INTERNAL_ERROR_S;
+ goto out;
+ }
+
+ if (di_key != selected_key) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Wrong key in use: %d instead of %d\n\n",
+ selected_key, di_key);
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ if (sym_ctx->mode == FSL_SYM_MODE_CBC) {
+ if ((sym_ctx->flags & FSL_SYM_CTX_LOAD)
+ && !(sym_ctx->flags & FSL_SYM_CTX_INIT)) {
+ iv = sym_ctx->context;
+ } else if ((sym_ctx->flags & FSL_SYM_CTX_INIT)
+ && !(sym_ctx->flags & FSL_SYM_CTX_LOAD)) {
+ iv = zeros;
+ } else {
+ /* Exactly one must be set! */
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+ }
+
+ /* Now run the data through the SCC */
+ scc_code = scc_crypt(length, in, iv,
+ encrypt ? SCC_ENCRYPT : SCC_DECRYPT,
+ (sym_ctx->mode == FSL_SYM_MODE_ECB)
+ ? SCC_ECB_MODE : SCC_CBC_MODE,
+ SCC_VERIFY_MODE_NONE, out, &count_out);
+ if (scc_code != SCC_RET_OK) {
+ ret = FSL_RETURN_INTERNAL_ERROR_S;
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("scc_code from scc_crypt() is %d\n", scc_code);
+#endif
+ goto out;
+ }
+
+ if ((sym_ctx->mode == FSL_SYM_MODE_CBC)
+ && (sym_ctx->flags & FSL_SYM_CTX_SAVE)) {
+ /* Save the context for the caller */
+ if (encrypt) {
+ /* Last ciphertext block ... */
+ memcpy(sym_ctx->context, out + length - 8, 8);
+ } else {
+ /* Last ciphertext block ... */
+ memcpy(sym_ctx->context, in + length - 8, 8);
+ }
+ }
+
+ ret = FSL_RETURN_OK_S;
+
+ out:
+ if (key_selected) {
+ (void)dryice_release_key_selection();
+ }
+
+ return ret;
+}
+
+EXPORT_SYMBOL(fsl_shw_symmetric_encrypt);
+/*!
+ * Compute symmetric encryption
+ *
+ *
+ * @param user_ctx
+ * @param key_info
+ * @param sym_ctx
+ * @param length
+ * @param pt
+ * @param ct
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_symmetric_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * pt, uint8_t * ct)
+{
+ fsl_shw_return_t ret;
+
+ ret = do_symmetric(user_ctx, key_info, sym_ctx, SYM_ENCRYPT,
+ length, pt, ct);
+
+ return ret;
+}
+
+EXPORT_SYMBOL(fsl_shw_symmetric_decrypt);
+/*!
+ * Compute symmetric decryption
+ *
+ *
+ * @param user_ctx
+ * @param key_info
+ * @param sym_ctx
+ * @param length
+ * @param pt
+ * @param ct
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_symmetric_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * ct, uint8_t * pt)
+{
+ fsl_shw_return_t ret;
+
+ ret = do_symmetric(user_ctx, key_info, sym_ctx, SYM_DECRYPT,
+ length, ct, pt);
+
+ return ret;
+}
+
+#else /* __KERNEL__ && DRYICE */
+
+fsl_shw_return_t fsl_shw_symmetric_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * pt, uint8_t * ct)
+{
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)sym_ctx;
+ (void)length;
+ (void)pt;
+ (void)ct;
+
+ return FSL_RETURN_ERROR_S;
+}
+
+fsl_shw_return_t fsl_shw_symmetric_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * ct, uint8_t * pt)
+{
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)sym_ctx;
+ (void)length;
+ (void)ct;
+ (void)pt;
+
+ return FSL_RETURN_ERROR_S;
+}
+
+#endif /* __KERNEL__ and DRYICE */
diff --git a/drivers/mxc/security/rng/fsl_shw_wrap.c b/drivers/mxc/security/rng/fsl_shw_wrap.c
new file mode 100644
index 000000000000..c7d6947296af
--- /dev/null
+++ b/drivers/mxc/security/rng/fsl_shw_wrap.c
@@ -0,0 +1,1301 @@
+/*
+ * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file fsl_shw_wrap.c
+ *
+ * This file implements Key-Wrap (Black Key) and Key Establishment functions of
+ * the FSL SHW API for the SHW (non-SAHARA) driver.
+ *
+ * This is the Black Key information:
+ *
+ * <ul>
+ * <li> Ownerid is an 8-byte, user-supplied, value to keep KEY
+ * confidential.</li>
+ * <li> KEY is a 1-32 byte value which starts in SCC RED RAM before
+ * wrapping, and ends up there on unwrap. Length is limited because of
+ * size of SCC1 RAM.</li>
+ * <li> KEY' is the encrypted KEY</li>
+ * <li> LEN is a 1-byte (for now) byte-length of KEY</li>
+ * <li> ALG is a 1-byte value for the algorithm which which the key is
+ * associated. Values are defined by the FSL SHW API</li>
+ * <li> FLAGS is a 1-byte value contain information like "this key is for
+ * software" (TBD)</li>
+ * <li> Ownerid, LEN, and ALG come from the user's "key_info" object, as does
+ * the slot number where KEY already is/will be.</li>
+ * <li> T is a Nonce</li>
+ * <li> T' is the encrypted T</li>
+ * <li> KEK is a Key-Encryption Key for the user's Key</li>
+ * <li> ICV is the "Integrity Check Value" for the wrapped key</li>
+ * <li> Black Key is the string of bytes returned as the wrapped key</li>
+ * <li> Wrap Key is the user's choice for encrypting the nonce. One of
+ * the Fused Key, the Random Key, or the XOR of the two.
+ * </ul>
+<table border="0">
+<tr><TD align="right">BLACK_KEY <TD width="3">=<TD>ICV | T' | LEN | ALG |
+ FLAGS | KEY'</td></tr>
+<tr><td>&nbsp;</td></tr>
+
+<tr><th>To Wrap</th></tr>
+<tr><TD align="right">T</td> <TD width="3">=</td> <TD>RND()<sub>16</sub>
+ </td></tr>
+<tr><TD align="right">KEK</td><TD width="3">=</td><TD>HASH<sub>sha256</sub>(T |
+ Ownerid)<sub>16</sub></td></tr>
+<tr><TD align="right">KEY'<TD width="3">=</td><TD>
+ TDES<sub>cbc-enc</sub>(Key=KEK, Data=KEY, IV=Ownerid)</td></tr>
+<tr><TD align="right">ICV</td><TD width="3">=</td><td>HMAC<sub>sha256</sub>
+ (Key=T, Data=Ownerid | LEN | ALG | FLAGS | KEY')<sub>16</sub></td></tr>
+<tr><TD align="right">T'</td><TD width="3">=</td><TD>TDES<sub>ecb-enc</sub>
+ (Key=Wrap_Key, IV=Ownerid, Data=T)</td></tr>
+
+<tr><td>&nbsp;</td></tr>
+
+<tr><th>To Unwrap</th></tr>
+<tr><TD align="right">T</td><TD width="3">=</td><TD>TDES<sub>ecb-dec</sub>
+ (Key=Wrap_Key, IV=Ownerid, Data=T')</td></tr>
+<tr><TD align="right">ICV</td><TD width="3">=</td><td>HMAC<sub>sha256</sub>
+ (Key=T, Data=Ownerid | LEN | ALG | FLAGS | KEY')<sub>16</sub></td></tr>
+<tr><TD align="right">KEK</td><TD width="3">=</td><td>HASH<sub>sha256</sub>
+ (T | Ownerid)<sub>16</sub></td></tr>
+<tr><TD align="right">KEY<TD width="3">=</td><TD>TDES<sub>cbc-dec</sub>
+ (Key=KEK, Data=KEY', IV=Ownerid)</td></tr>
+</table>
+
+ * This code supports two types of keys: Software Keys and keys destined for
+ * (or residing in) the DryIce Programmed Key Register.
+ *
+ * Software Keys go to / from the keystore.
+ *
+ * PK keys go to / from the DryIce Programmed Key Register.
+ *
+ * This code only works on a platform with DryIce. "software" keys go into
+ * the keystore. "Program" keys go to the DryIce Programmed Key Register.
+ * As far as this code is concerned, the size of that register is 21 bytes,
+ * the size of a 3DES key with parity stripped.
+ *
+ * The maximum key size supported for wrapped/unwrapped keys depends upon
+ * LENGTH_LENGTH. Currently, it is one byte, so the maximum key size is
+ * 255 bytes. However, key objects cannot currently hold a key of this
+ * length, so a smaller key size is the max.
+ */
+
+#include "fsl_platform.h"
+
+/* This code only works in kernel mode */
+
+#include "shw_driver.h"
+#ifdef DIAG_SECURITY_FUNC
+#include "apihelp.h"
+#endif
+
+#if defined(__KERNEL__) && defined(FSL_HAVE_DRYICE)
+
+#include "../dryice.h"
+#include <linux/mxc_scc_driver.h>
+
+#include "portable_os.h"
+#include "fsl_shw_keystore.h"
+
+#include <diagnostic.h>
+
+#include "shw_hmac.h"
+#include "shw_hash.h"
+
+#define ICV_LENGTH 16
+#define T_LENGTH 16
+#define KEK_LENGTH 21
+#define LENGTH_LENGTH 1
+#define ALGORITHM_LENGTH 1
+#define FLAGS_LENGTH 1
+
+/* ICV | T' | LEN | ALG | FLAGS | KEY' */
+#define ICV_OFFSET 0
+#define T_PRIME_OFFSET (ICV_OFFSET + ICV_LENGTH)
+#define LENGTH_OFFSET (T_PRIME_OFFSET + T_LENGTH)
+#define ALGORITHM_OFFSET (LENGTH_OFFSET + LENGTH_LENGTH)
+#define FLAGS_OFFSET (ALGORITHM_OFFSET + ALGORITHM_LENGTH)
+#define KEY_PRIME_OFFSET (FLAGS_OFFSET + FLAGS_LENGTH)
+
+#define FLAGS_SW_KEY 0x01
+
+#define LENGTH_PATCH 8
+#define LENGTH_PATCH_MASK (LENGTH_PATCH - 1)
+
+/*! rounded up from 168 bits to the next word size */
+#define HW_KEY_LEN_WORDS_BITS 192
+
+/*!
+ * Round a key length up to the TDES block size
+ *
+ * @param len Length of key, in bytes
+ *
+ * @return Length rounded up, if necessary
+ */
+#define ROUND_LENGTH(len) \
+({ \
+ uint32_t orig_len = len; \
+ uint32_t new_len; \
+ \
+ if ((orig_len & LENGTH_PATCH_MASK) != 0) { \
+ new_len = (orig_len + LENGTH_PATCH \
+ - (orig_len & LENGTH_PATCH_MASK)); \
+ } \
+ else { \
+ new_len = orig_len; \
+ } \
+ \
+ new_len; \
+})
+
+/* This is the system keystore object */
+extern fsl_shw_kso_t system_keystore;
+
+#ifdef DIAG_SECURITY_FUNC
+static void dump(const char *name, const uint8_t * data, unsigned int len)
+{
+ os_printk("%s: ", name);
+ while (len > 0) {
+ os_printk("%02x ", (unsigned)*data++);
+ len--;
+ }
+ os_printk("\n");
+}
+#endif
+
+/*
+ * For testing of the algorithm implementation,, the DO_REPEATABLE_WRAP flag
+ * causes the T_block to go into the T field during a wrap operation. This
+ * will make the black key value repeatable (for a given SCC secret key, or
+ * always if the default key is in use).
+ *
+ * Normally, a random sequence is used.
+ */
+#ifdef DO_REPEATABLE_WRAP
+/*!
+ * Block of zeroes which is maximum Symmetric block size, used for
+ * initializing context register, etc.
+ */
+static uint8_t T_block[16] = {
+ 0x42, 0, 0, 0x42, 0x42, 0, 0, 0x42,
+ 0x42, 0, 0, 0x42, 0x42, 0, 0, 0x42
+};
+#endif
+
+EXPORT_SYMBOL(fsl_shw_establish_key);
+EXPORT_SYMBOL(fsl_shw_read_key);
+EXPORT_SYMBOL(fsl_shw_extract_key);
+EXPORT_SYMBOL(fsl_shw_release_key);
+
+extern fsl_shw_return_t alloc_slot(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info);
+
+extern fsl_shw_return_t load_slot(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ const uint8_t * key);
+
+extern fsl_shw_return_t dealloc_slot(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info);
+
+/*!
+ * Initalialize SKO and SCCO used for T <==> T' cipher operation
+ *
+ * @param wrap_key Which wrapping key user wants
+ * @param key_info Key object for selecting wrap key
+ * @param wrap_ctx Sym Context object for doing the cipher op
+ */
+static inline void init_wrap_key(fsl_shw_pf_key_t wrap_key,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * wrap_ctx)
+{
+ fsl_shw_sko_init_pf_key(key_info, FSL_KEY_ALG_TDES, wrap_key);
+ fsl_shw_scco_init(wrap_ctx, FSL_KEY_ALG_TDES, FSL_SYM_MODE_ECB);
+}
+
+/*!
+ * Insert descriptors to calculate ICV = HMAC(key=T, data=LEN|ALG|KEY')
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param T Location of nonce (length is T_LENGTH bytes)
+ * @param userid Location of userid/ownerid
+ * @param userid_len Length, in bytes of @c userid
+ * @param black_key Beginning of Black Key region
+ * @param key_length Number of bytes of key' there are in @c black_key
+ * @param[out] hmac Location to store ICV. Will be tagged "USES" so
+ * sf routines will not try to free it.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static fsl_shw_return_t calc_icv(const uint8_t * T,
+ const uint8_t * userid,
+ unsigned int userid_len,
+ const uint8_t * black_key,
+ uint32_t key_length, uint8_t * hmac)
+{
+ fsl_shw_return_t code;
+ shw_hmac_state_t hmac_state;
+
+ /* Load up T as key for the HMAC */
+ code = shw_hmac_init(&hmac_state, T, T_LENGTH);
+ if (code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Previous step loaded key; Now set up to hash the data */
+
+ /* Input - start with ownerid */
+ code = shw_hmac_update(&hmac_state, userid, userid_len);
+ if (code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Still input - Append black-key fields len, alg, key' */
+ code = shw_hmac_update(&hmac_state,
+ (void *)black_key + LENGTH_OFFSET,
+ (LENGTH_LENGTH
+ + ALGORITHM_LENGTH
+ + FLAGS_LENGTH + key_length));
+ if (code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Output - computed ICV/HMAC */
+ code = shw_hmac_final(&hmac_state, hmac, ICV_LENGTH);
+ if (code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ out:
+
+ return code;
+} /* calc_icv */
+
+/*!
+ * Compute and return the KEK (Key Encryption Key) from the inputs
+ *
+ * @param userid The user's 'secret' for the key
+ * @param userid_len Length, in bytes of @c userid
+ * @param T The nonce
+ * @param[out] kek Location to store the computed KEK. It will
+ * be 21 bytes long.
+ *
+ * @return the usual error code
+ */
+static fsl_shw_return_t calc_kek(const uint8_t * userid,
+ unsigned int userid_len,
+ const uint8_t * T, uint8_t * kek)
+{
+ fsl_shw_return_t code = FSL_RETURN_INTERNAL_ERROR_S;
+ shw_hash_state_t hash_state;
+
+ code = shw_hash_init(&hash_state, FSL_HASH_ALG_SHA256);
+ if (code != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Hash init failed: %s\n", fsl_error_string(code));
+#endif
+ goto out;
+ }
+
+ code = shw_hash_update(&hash_state, T, T_LENGTH);
+ if (code != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Hash for T failed: %s\n",
+ fsl_error_string(code));
+#endif
+ goto out;
+ }
+
+ code = shw_hash_update(&hash_state, userid, userid_len);
+ if (code != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Hash for userid failed: %s\n",
+ fsl_error_string(code));
+#endif
+ goto out;
+ }
+
+ code = shw_hash_final(&hash_state, kek, KEK_LENGTH);
+ if (code != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Could not extract kek: %s\n",
+ fsl_error_string(code));
+#endif
+ goto out;
+ }
+
+#if KEK_LENGTH != 21
+ {
+ uint8_t permuted_kek[21];
+
+ fsl_shw_permute1_bytes(kek, permuted_kek, KEK_LENGTH / 8);
+ memcpy(kek, permuted_kek, 21);
+ memset(permuted_kek, 0, sizeof(permuted_kek));
+ }
+#endif
+
+#ifdef DIAG_SECURITY_FUNC
+ dump("kek", kek, 21);
+#endif
+
+ out:
+
+ return code;
+} /* end fn calc_kek */
+
+/*!
+ * Validate user's wrap key selection
+ *
+ * @param wrap_key The user's desired wrapping key
+ */
+static fsl_shw_return_t check_wrap_key(fsl_shw_pf_key_t wrap_key)
+{
+ /* unable to use desired key */
+ fsl_shw_return_t ret = FSL_RETURN_NO_RESOURCE_S;
+
+ if ((wrap_key != FSL_SHW_PF_KEY_IIM) &&
+ (wrap_key != FSL_SHW_PF_KEY_RND) &&
+ (wrap_key != FSL_SHW_PF_KEY_IIM_RND)) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Invalid wrap_key in key wrap/unwrap attempt");
+#endif
+ goto out;
+ }
+ ret = FSL_RETURN_OK_S;
+
+ out:
+ return ret;
+} /* end fn check_wrap_key */
+
+/*!
+ * Perform unwrapping of a black key into a RED slot
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] key_info The information about the key to be which will
+ * be unwrapped... key length, slot info, etc.
+ * @param black_key Encrypted key
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static fsl_shw_return_t unwrap(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ const uint8_t * black_key)
+{
+ fsl_shw_return_t ret;
+ uint8_t hmac[ICV_LENGTH];
+ uint8_t T[T_LENGTH];
+ uint8_t kek[KEK_LENGTH + 20];
+ int key_length = black_key[LENGTH_OFFSET];
+ int rounded_key_length = ROUND_LENGTH(key_length);
+ uint8_t key[rounded_key_length];
+ fsl_shw_sko_t t_key_info;
+ fsl_shw_scco_t t_key_ctx;
+ fsl_shw_sko_t kek_key_info;
+ fsl_shw_scco_t kek_ctx;
+ int unwrapping_sw_key = key_info->flags & FSL_SKO_KEY_SW_KEY;
+ int pk_needs_restoration = 0; /* bool */
+ unsigned original_key_length = key_info->key_length;
+ int pk_was_held = 0;
+ uint8_t current_pk[21];
+ di_return_t di_code;
+
+ ret = check_wrap_key(user_ctx->wrap_key);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ if (black_key == NULL) {
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+#ifdef DIAG_SECURITY_FUNC
+ dump("black", black_key, KEY_PRIME_OFFSET + key_length);
+#endif
+ /* Validate SW flags to prevent misuse */
+ if ((key_info->flags & FSL_SKO_KEY_SW_KEY)
+ && !(black_key[FLAGS_OFFSET] & FLAGS_SW_KEY)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ /* Compute T = 3des-dec-ecb(wrap_key, T') */
+ init_wrap_key(user_ctx->wrap_key, &t_key_info, &t_key_ctx);
+ ret = fsl_shw_symmetric_decrypt(user_ctx, &t_key_info, &t_key_ctx,
+ T_LENGTH,
+ black_key + T_PRIME_OFFSET, T);
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Recovery of nonce (T) failed");
+#endif /*DIAG_SECURITY_FUNC */
+ goto out;
+ }
+
+ /* Compute ICV = HMAC(T, ownerid | len | alg | flags | key' */
+ ret = calc_icv(T, (uint8_t *) & key_info->userid,
+ sizeof(key_info->userid),
+ black_key, original_key_length, hmac);
+
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Calculation of ICV failed");
+#endif /*DIAG_SECURITY_FUNC */
+ goto out;
+ }
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Validating MAC of wrapped key");
+#endif
+
+ /* Check computed ICV against value in Black Key */
+ if (memcmp(black_key + ICV_OFFSET, hmac, ICV_LENGTH) != 0) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Computed ICV fails validation\n");
+#endif
+ ret = FSL_RETURN_AUTH_FAILED_S;
+ goto out;
+ }
+
+ /* Compute KEK = SHA256(T | ownerid). */
+ ret = calc_kek((uint8_t *) & key_info->userid, sizeof(key_info->userid),
+ T, kek);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ if (unwrapping_sw_key) {
+ di_code = dryice_get_programmed_key(current_pk, 8 * 21);
+ if (di_code != DI_SUCCESS) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Could not save current PK: %s\n",
+ di_error_string(di_code));
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+ }
+
+ /*
+ * "Establish" the KEK in the PK. If the PK was held and unwrapping a
+ * software key, then release it and try again, but remember that we need
+ * to leave it 'held' if we are unwrapping a software key.
+ *
+ * If the PK is held while we are unwrapping a key for the PK, then
+ * the user didn't call release, so gets an error.
+ */
+ di_code = dryice_set_programmed_key(kek, 8 * 21, 0);
+ if ((di_code == DI_ERR_INUSE) && unwrapping_sw_key) {
+ /* Temporarily reprogram the PK out from under the user */
+ pk_was_held = 1;
+ dryice_release_programmed_key();
+ di_code = dryice_set_programmed_key(kek, 8 * 21, 0);
+ }
+ if (di_code != DI_SUCCESS) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Could not program KEK: %s\n",
+ di_error_string(di_code));
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ if (unwrapping_sw_key) {
+ pk_needs_restoration = 1;
+ }
+ dryice_release_programmed_key(); /* Because of previous 'set' */
+
+ /* Compute KEY = TDES-decrypt(KEK, KEY') */
+ fsl_shw_sko_init_pf_key(&kek_key_info, FSL_KEY_ALG_TDES,
+ FSL_SHW_PF_KEY_PRG);
+ fsl_shw_sko_set_key_length(&kek_key_info, KEK_LENGTH);
+
+ fsl_shw_scco_init(&kek_ctx, FSL_KEY_ALG_TDES, FSL_SYM_MODE_CBC);
+ fsl_shw_scco_set_flags(&kek_ctx, FSL_SYM_CTX_LOAD);
+ fsl_shw_scco_set_context(&kek_ctx, (uint8_t *) & key_info->userid);
+#ifdef DIAG_SECURITY_FUNC
+ dump("KEY'", black_key + KEY_PRIME_OFFSET, rounded_key_length);
+#endif
+ ret = fsl_shw_symmetric_decrypt(user_ctx, &kek_key_info, &kek_ctx,
+ rounded_key_length,
+ black_key + KEY_PRIME_OFFSET, key);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+#ifdef DIAG_SECURITY_FUNC
+ dump("KEY", key, original_key_length);
+#endif
+ /* Now either put key into PK or into a slot */
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ ret = load_slot(user_ctx, key_info, key);
+ } else {
+ /*
+ * Since we have just unwrapped a program key, it had
+ * to have been wrapped as a program key, so it must
+ * be 168 bytes long and permuted ...
+ */
+ ret = dryice_set_programmed_key(key, 8 * key_length, 0);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ }
+
+ out:
+ key_info->key_length = original_key_length;
+
+ if (pk_needs_restoration) {
+ di_code = dryice_set_programmed_key(current_pk, 8 * 21, 0);
+ }
+
+ if (!pk_was_held) {
+ dryice_release_programmed_key();
+ }
+
+ /* Erase tracks of confidential data */
+ memset(T, 0, T_LENGTH);
+ memset(key, 0, rounded_key_length);
+ memset(current_pk, 0, sizeof(current_pk));
+ memset(&t_key_info, 0, sizeof(t_key_info));
+ memset(&t_key_ctx, 0, sizeof(t_key_ctx));
+ memset(&kek_key_info, 0, sizeof(kek_key_info));
+ memset(&kek_ctx, 0, sizeof(kek_ctx));
+ memset(kek, 0, KEK_LENGTH);
+
+ return ret;
+} /* unwrap */
+
+/*!
+ * Perform wrapping of a black key from a RED slot (or the PK register)
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] key_info The information about the key to be which will
+ * be wrapped... key length, slot info, etc.
+ * @param black_key Place to store encrypted key
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static fsl_shw_return_t wrap(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info, uint8_t * black_key)
+{
+ fsl_shw_return_t ret = FSL_RETURN_OK_S;
+ fsl_shw_sko_t t_key_info; /* for holding T */
+ fsl_shw_scco_t t_key_ctx;
+ fsl_shw_sko_t kek_key_info;
+ fsl_shw_scco_t kek_ctx;
+ unsigned original_key_length = key_info->key_length;
+ unsigned rounded_key_length;
+ uint8_t T[T_LENGTH];
+ uint8_t kek[KEK_LENGTH + 20];
+ uint8_t *red_key = 0;
+ int red_key_malloced = 0; /* bool */
+ int pk_was_held = 0; /* bool */
+ uint8_t saved_pk[21];
+ uint8_t pk_needs_restoration; /* bool */
+ di_return_t di_code;
+
+ ret = check_wrap_key(user_ctx->wrap_key);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ if (black_key == NULL) {
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ if (key_info->flags & FSL_SKO_KEY_SELECT_PF_KEY) {
+ if ((key_info->pf_key != FSL_SHW_PF_KEY_PRG)
+ && (key_info->pf_key != FSL_SHW_PF_KEY_IIM_PRG)) {
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+ } else {
+ if (!(key_info->flags & FSL_SKO_KEY_ESTABLISHED)) {
+ ret = FSL_RETURN_BAD_FLAG_S; /* not established! */
+ goto out;
+ }
+ }
+
+ black_key[ALGORITHM_OFFSET] = key_info->algorithm;
+
+#ifndef DO_REPEATABLE_WRAP
+ /* Compute T = RND() */
+ ret = fsl_shw_get_random(user_ctx, T_LENGTH, T);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+#else
+ memcpy(T, T_block, T_LENGTH);
+#endif
+
+ /* Compute KEK = SHA256(T | ownerid). */
+ ret = calc_kek((uint8_t *) & key_info->userid, sizeof(key_info->userid),
+ T, kek);
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Calculation of KEK failed\n");
+#endif /*DIAG_SECURITY_FUNC */
+ goto out;
+ }
+
+ rounded_key_length = ROUND_LENGTH(original_key_length);
+
+ di_code = dryice_get_programmed_key(saved_pk, 8 * 21);
+ if (di_code != DI_SUCCESS) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Could not save current PK: %s\n",
+ di_error_string(di_code));
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ /*
+ * Load KEK into DI PKR. Note that we are NOT permuting it before loading,
+ * so we are using it as though it is a 168-bit key ready for the SCC.
+ */
+ di_code = dryice_set_programmed_key(kek, 8 * 21, 0);
+ if (di_code == DI_ERR_INUSE) {
+ /* Temporarily reprogram the PK out from under the user */
+ pk_was_held = 1;
+ dryice_release_programmed_key();
+ di_code = dryice_set_programmed_key(kek, 8 * 21, 0);
+ }
+ if (di_code != DI_SUCCESS) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Could not program KEK: %s\n",
+ di_error_string(di_code));
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+ pk_needs_restoration = 1;
+ dryice_release_programmed_key();
+
+ /* Find red key */
+ if (key_info->flags & FSL_SKO_KEY_SELECT_PF_KEY) {
+ black_key[LENGTH_OFFSET] = 21;
+ rounded_key_length = 24;
+
+ red_key = saved_pk;
+ } else {
+ black_key[LENGTH_OFFSET] = key_info->key_length;
+
+ red_key = os_alloc_memory(key_info->key_length, 0);
+ if (red_key == NULL) {
+ ret = FSL_RETURN_NO_RESOURCE_S;
+ goto out;
+ }
+ red_key_malloced = 1;
+
+ ret = fsl_shw_read_key(user_ctx, key_info, red_key);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ }
+
+#ifdef DIAG_SECURITY_FUNC
+ dump("KEY", red_key, black_key[LENGTH_OFFSET]);
+#endif
+ /* Compute KEY' = TDES-encrypt(KEK, KEY) */
+ fsl_shw_sko_init_pf_key(&kek_key_info, FSL_KEY_ALG_TDES,
+ FSL_SHW_PF_KEY_PRG);
+ fsl_shw_sko_set_key_length(&kek_key_info, KEK_LENGTH);
+
+ fsl_shw_scco_init(&kek_ctx, FSL_KEY_ALG_TDES, FSL_SYM_MODE_CBC);
+ fsl_shw_scco_set_flags(&kek_ctx, FSL_SYM_CTX_LOAD);
+ fsl_shw_scco_set_context(&kek_ctx, (uint8_t *) & key_info->userid);
+ ret = fsl_shw_symmetric_encrypt(user_ctx, &kek_key_info, &kek_ctx,
+ rounded_key_length,
+ red_key, black_key + KEY_PRIME_OFFSET);
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Encryption of KEY failed\n");
+#endif /*DIAG_SECURITY_FUNC */
+ goto out;
+ }
+
+ /* Set up flags info */
+ black_key[FLAGS_OFFSET] = 0;
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ black_key[FLAGS_OFFSET] |= FLAGS_SW_KEY;
+ }
+#ifdef DIAG_SECURITY_FUNC
+ dump("KEY'", black_key + KEY_PRIME_OFFSET, rounded_key_length);
+#endif
+ /* Compute and store ICV into Black Key */
+ ret = calc_icv(T,
+ (uint8_t *) & key_info->userid,
+ sizeof(key_info->userid),
+ black_key, original_key_length, black_key + ICV_OFFSET);
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Calculation of ICV failed\n");
+#endif /*DIAG_SECURITY_FUNC */
+ goto out;
+ }
+
+ /* Compute T' = 3des-enc-ecb(wrap_key, T); Result goes to Black Key */
+ init_wrap_key(user_ctx->wrap_key, &t_key_info, &t_key_ctx);
+ ret = fsl_shw_symmetric_encrypt(user_ctx, &t_key_info, &t_key_ctx,
+ T_LENGTH,
+ T, black_key + T_PRIME_OFFSET);
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Encryption of nonce failed");
+#endif
+ goto out;
+ }
+#ifdef DIAG_SECURITY_FUNC
+ dump("black", black_key, KEY_PRIME_OFFSET + black_key[LENGTH_OFFSET]);
+#endif
+
+ out:
+ if (pk_needs_restoration) {
+ dryice_set_programmed_key(saved_pk, 8 * 21, 0);
+ }
+
+ if (!pk_was_held) {
+ dryice_release_programmed_key();
+ }
+
+ if (red_key_malloced) {
+ memset(red_key, 0, key_info->key_length);
+ os_free_memory(red_key);
+ }
+
+ key_info->key_length = original_key_length;
+
+ /* Erase tracks of confidential data */
+ memset(T, 0, T_LENGTH);
+ memset(&t_key_info, 0, sizeof(t_key_info));
+ memset(&t_key_ctx, 0, sizeof(t_key_ctx));
+ memset(&kek_key_info, 0, sizeof(kek_key_info));
+ memset(&kek_ctx, 0, sizeof(kek_ctx));
+ memset(kek, 0, sizeof(kek));
+ memset(saved_pk, 0, sizeof(saved_pk));
+
+ return ret;
+} /* wrap */
+
+static fsl_shw_return_t create(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+ unsigned key_length = key_info->key_length;
+ di_return_t di_code;
+
+ if (!(key_info->flags & FSL_SKO_KEY_SW_KEY)) {
+ /* Must be creating key for PK */
+ if ((key_info->algorithm != FSL_KEY_ALG_TDES) ||
+ ((key_info->key_length != 16)
+ && (key_info->key_length != 21) /* permuted 168-bit key */
+ &&(key_info->key_length != 24))) {
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ key_length = 21; /* 168-bit PK */
+ }
+
+ /* operational block */
+ {
+ uint8_t key_value[key_length];
+
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Creating random key\n");
+#endif
+ ret = fsl_shw_get_random(user_ctx, key_length, key_value);
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("get_random for CREATE KEY failed\n");
+#endif
+ goto out;
+ }
+
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ ret = load_slot(user_ctx, key_info, key_value);
+ } else {
+ di_code =
+ dryice_set_programmed_key(key_value, 8 * key_length,
+ 0);
+ if (di_code != 0) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("di set_pk failed: %s\n",
+ di_error_string(di_code));
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+ ret = FSL_RETURN_OK_S;
+ }
+ memset(key_value, 0, key_length);
+ } /* end operational block */
+
+#ifdef DIAG_SECURITY_FUNC
+ if (ret != FSL_RETURN_OK_S) {
+ LOG_DIAG("Loading random key failed");
+ }
+#endif
+
+ out:
+
+ return ret;
+} /* end fn create */
+
+static fsl_shw_return_t accept(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info, const uint8_t * key)
+{
+ uint8_t permuted_key[21];
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ if (key == NULL) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("ACCEPT: Red Key is NULL");
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+#ifdef DIAG_SECURITY_FUNC
+ dump("red", key, key_info->key_length);
+#endif
+ /* Only SW keys go into the keystore */
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+
+ /* Copy in safe number of bytes of Red key */
+ ret = load_slot(user_ctx, key_info, key);
+ } else { /* not SW key */
+ di_return_t di_ret;
+
+ /* Only 3DES PGM key types can be established */
+ if (((key_info->pf_key != FSL_SHW_PF_KEY_PRG)
+ && (key_info->pf_key != FSL_SHW_PF_KEY_IIM_PRG))
+ || (key_info->algorithm != FSL_KEY_ALG_TDES)) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS
+ ("ACCEPT: Failed trying to establish non-PRG"
+ " or invalid 3DES Key: iim%d, iim_prg%d, alg%d\n",
+ (key_info->pf_key != FSL_SHW_PF_KEY_PRG),
+ (key_info->pf_key != FSL_SHW_PF_KEY_IIM_PRG),
+ (key_info->algorithm != FSL_KEY_ALG_TDES));
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+ if ((key_info->key_length != 16)
+ && (key_info->key_length != 21)
+ && (key_info->key_length != 24)) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("ACCEPT: Failed trying to establish"
+ " invalid 3DES Key: len=%d (%d)\n",
+ key_info->key_length,
+ ((key_info->key_length != 16)
+ && (key_info->key_length != 21)
+ && (key_info->key_length != 24)));
+#endif
+ ret = FSL_RETURN_BAD_KEY_LENGTH_S;
+ goto out;
+ }
+
+ /* Convert key into 168-bit value and put it into PK */
+ if (key_info->key_length != 21) {
+ fsl_shw_permute1_bytes(key, permuted_key,
+ key_info->key_length / 8);
+ di_ret =
+ dryice_set_programmed_key(permuted_key, 168, 0);
+ } else {
+ /* Already permuted ! */
+ di_ret = dryice_set_programmed_key(key, 168, 0);
+ }
+ if (di_ret != DI_SUCCESS) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS
+ ("ACCEPT: DryIce error setting Program Key: %s",
+ di_error_string(di_ret));
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+ }
+
+ ret = FSL_RETURN_OK_S;
+
+ out:
+ memset(permuted_key, 0, 21);
+
+ return ret;
+} /* end fn accept */
+
+/*!
+ * Place a key into a protected location for use only by cryptographic
+ * algorithms.
+ *
+ * This only needs to be used to a) unwrap a key, or b) set up a key which
+ * could be wrapped by calling #fsl_shw_extract_key() at some later time).
+ *
+ * The protected key will not be available for use until this operation
+ * successfully completes.
+ *
+ * @bug This whole discussion needs review.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] key_info The information about the key to be which will
+ * be established. In the create case, the key
+ * length must be set.
+ * @param establish_type How @a key will be interpreted to establish a
+ * key for use.
+ * @param key If @a establish_type is #FSL_KEY_WRAP_UNWRAP,
+ * this is the location of a wrapped key. If
+ * @a establish_type is #FSL_KEY_WRAP_CREATE, this
+ * parameter can be @a NULL. If @a establish_type
+ * is #FSL_KEY_WRAP_ACCEPT, this is the location
+ * of a plaintext key.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_establish_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_key_wrap_t establish_type,
+ const uint8_t * key)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+ unsigned original_key_length = key_info->key_length;
+ unsigned rounded_key_length;
+ unsigned slot_allocated = 0;
+
+ /* For now, only blocking mode calls are supported */
+ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("%s: Non-blocking call not supported\n",
+ __FUNCTION__);
+#endif
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ /*
+ HW keys are always 'established', but otherwise do not allow user
+ * to establish over the top of an established key.
+ */
+ if ((key_info->flags & FSL_SKO_KEY_ESTABLISHED)
+ && !(key_info->flags & FSL_SKO_KEY_SELECT_PF_KEY)) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("%s: Key already established\n", __FUNCTION__);
+#endif
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ /* @bug VALIDATE KEY flags here -- SW or PRG/IIM_PRG */
+
+ /* Write operations into SCC memory require word-multiple number of
+ * bytes. For ACCEPT and CREATE functions, the key length may need
+ * to be rounded up. Calculate. */
+ if (LENGTH_PATCH && (original_key_length & LENGTH_PATCH_MASK) != 0) {
+ rounded_key_length = original_key_length + LENGTH_PATCH
+ - (original_key_length & LENGTH_PATCH_MASK);
+ } else {
+ rounded_key_length = original_key_length;
+ }
+
+ /* SW keys need a place to live */
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ ret = alloc_slot(user_ctx, key_info);
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Slot allocation failed\n");
+#endif
+ goto out;
+ }
+ slot_allocated = 1;
+ }
+
+ switch (establish_type) {
+ case FSL_KEY_WRAP_CREATE:
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Creating random key\n");
+#endif
+ ret = create(user_ctx, key_info);
+ break;
+
+ case FSL_KEY_WRAP_ACCEPT:
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Accepting plaintext key\n");
+#endif
+ ret = accept(user_ctx, key_info, key);
+ break;
+
+ case FSL_KEY_WRAP_UNWRAP:
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Unwrapping wrapped key\n");
+#endif
+ ret = unwrap(user_ctx, key_info, key);
+ break;
+
+ default:
+ ret = FSL_RETURN_BAD_FLAG_S;
+ break;
+ } /* switch */
+
+ out:
+ if (ret != FSL_RETURN_OK_S) {
+ if (slot_allocated) {
+ (void)dealloc_slot(user_ctx, key_info);
+ }
+ key_info->flags &= ~FSL_SKO_KEY_ESTABLISHED;
+ } else {
+ key_info->flags |= FSL_SKO_KEY_ESTABLISHED;
+ }
+
+ return ret;
+} /* end fn fsl_shw_establish_key */
+
+/*!
+ * Wrap a key and retrieve the wrapped value.
+ *
+ * A wrapped key is a key that has been cryptographically obscured. It is
+ * only able to be used with #fsl_shw_establish_key().
+ *
+ * This function will also release a software key (see #fsl_shw_release_key())
+ * so it must be re-established before reuse. This is not true of PGM keys.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The information about the key to be deleted.
+ * @param[out] covered_key The location to store the 48-octet wrapped key.
+ * (This size is based upon the maximum key size
+ * of 32 octets).
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_extract_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ uint8_t * covered_key)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ /* For now, only blocking mode calls are supported */
+ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Wrapping a key\n");
+#endif
+
+ if (!(key_info->flags & FSL_SKO_KEY_ESTABLISHED)) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("%s: Key not established\n", __FUNCTION__);
+#endif
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+ /* Verify that a SW key info really belongs to a SW key */
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ /* ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;*/
+ }
+
+ ret = wrap(user_ctx, key_info, covered_key);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ /* Need to deallocate on successful extraction */
+ (void)dealloc_slot(user_ctx, key_info);
+ /* Mark key not available in the flags */
+ key_info->flags &=
+ ~(FSL_SKO_KEY_ESTABLISHED | FSL_SKO_KEY_PRESENT);
+ memset(key_info->key, 0, sizeof(key_info->key));
+ }
+
+ out:
+ return ret;
+} /* end fn fsl_shw_extract_key */
+
+/*!
+ * De-establish a key so that it can no longer be accessed.
+ *
+ * The key will need to be re-established before it can again be used.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The information about the key to be deleted.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_release_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ /* For now, only blocking mode calls are supported */
+ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Not in blocking mode\n");
+#endif
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Releasing a key\n");
+#endif
+
+ if (!(key_info->flags & FSL_SKO_KEY_ESTABLISHED)) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Key not established\n");
+#endif
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ (void)dealloc_slot(user_ctx, key_info);
+ /* Turn off 'established' flag */
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("dealloc_slot() called\n");
+#endif
+ key_info->flags &= ~FSL_SKO_KEY_ESTABLISHED;
+ ret = FSL_RETURN_OK_S;
+ goto out;
+ }
+
+ if ((key_info->pf_key == FSL_SHW_PF_KEY_PRG)
+ || (key_info->pf_key == FSL_SHW_PF_KEY_IIM_PRG)) {
+ di_return_t di_ret;
+
+ di_ret = dryice_release_programmed_key();
+ if (di_ret != DI_SUCCESS) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS
+ ("dryice_release_programmed_key() failed: %d\n",
+ di_ret);
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+ } else {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Neither SW nor HW key\n");
+#endif
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ ret = FSL_RETURN_OK_S;
+
+ out:
+ return ret;
+} /* end fn fsl_shw_release_key */
+
+fsl_shw_return_t fsl_shw_read_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info, uint8_t * key)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+
+ /* Only blocking mode calls are supported */
+ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+ printk("Reading a key\n");
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Reading a key");
+#endif
+ if (key_info->flags & FSL_SKO_KEY_PRESENT) {
+ memcpy(key_info->key, key, key_info->key_length);
+ ret = FSL_RETURN_OK_S;
+ } else if (key_info->flags & FSL_SKO_KEY_ESTABLISHED) {
+ printk("key established\n");
+ if (key_info->keystore == NULL) {
+ printk("keystore is null\n");
+ /* First verify that the key access is valid */
+ ret =
+ system_keystore.slot_verify_access(system_keystore.
+ user_data,
+ key_info->userid,
+ key_info->
+ handle);
+
+ printk("key in system keystore\n");
+
+ /* Key is in system keystore */
+ ret = keystore_slot_read(&system_keystore,
+ key_info->userid,
+ key_info->handle,
+ key_info->key_length, key);
+ } else {
+ printk("key goes in user keystore.\n");
+ /* Key goes in user keystore */
+ ret = keystore_slot_read(key_info->keystore,
+ key_info->userid,
+ key_info->handle,
+ key_info->key_length, key);
+ }
+ }
+
+ out:
+ return ret;
+} /* end fn fsl_shw_read_key */
+
+#else /* __KERNEL__ && DRYICE */
+
+/* User mode -- these functions are unsupported */
+
+fsl_shw_return_t fsl_shw_establish_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_key_wrap_t establish_type,
+ const uint8_t * key)
+{
+ (void)user_ctx;
+ (void)key_info;
+ (void)establish_type;
+ (void)key;
+
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t fsl_shw_extract_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ uint8_t * covered_key)
+{
+ (void)user_ctx;
+ (void)key_info;
+ (void)covered_key;
+
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t fsl_shw_release_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info)
+{
+ (void)user_ctx;
+ (void)key_info;
+
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t fsl_shw_read_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info, uint8_t * key)
+{
+ (void)user_ctx;
+ (void)key_info;
+ (void)key;
+
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+#endif /* __KERNEL__ && DRYICE */
diff --git a/drivers/mxc/security/rng/include/rng_driver.h b/drivers/mxc/security/rng/include/rng_driver.h
new file mode 100644
index 000000000000..e760a14f2ded
--- /dev/null
+++ b/drivers/mxc/security/rng/include/rng_driver.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifndef RNG_DRIVER_H
+#define RNG_DRIVER_H
+
+#include "shw_driver.h"
+
+/* This is a Linux flag meaning 'compiling kernel code'... */
+#ifndef __KERNEL__
+#include <inttypes.h>
+#include <stdlib.h>
+#include <memory.h>
+#else
+#include "../../sahara2/include/portable_os.h"
+#endif
+
+#include "../../sahara2/include/fsl_platform.h"
+
+/*! @file rng_driver.h
+ *
+ * @brief Header file to use the RNG driver.
+ *
+ * @ingroup RNG
+ */
+
+#if defined(FSL_HAVE_RNGA)
+
+#include "rng_rnga.h"
+
+#elif defined(FSL_HAVE_RNGB) || defined(FSL_HAVE_RNGC)
+
+#include "rng_rngc.h"
+
+#else /* neither RNGA, RNGB, nor RNGC */
+
+#error NO_RNG_TYPE_IDENTIFIED
+
+#endif
+
+/*****************************************************************************
+ * Enumerations
+ *****************************************************************************/
+
+/*! Values from Version ID register */
+enum rng_type {
+ /*! Type RNGA. */
+ RNG_TYPE_RNGA = 0,
+ /*! Type RNGB. */
+ RNG_TYPE_RNGB = 1,
+ /*! Type RNGC */
+ RNG_TYPE_RNGC = 2
+};
+
+/*!
+ * Return values (error codes) for kernel register interface functions
+ */
+typedef enum rng_return {
+ RNG_RET_OK = 0, /*!< Function succeeded */
+ RNG_RET_FAIL /*!< Non-specific failure */
+} rng_return_t;
+
+/*****************************************************************************
+ * Data Structures
+ *****************************************************************************/
+/*!
+ * An entry in the RNG Work Queue. Based upon standard SHW queue entry.
+ *
+ * This entry also gets saved (for non-blocking requests) in the user's result
+ * pool. When the user picks up the request, the final processing (copy from
+ * data_local to data_user) will get made if status was good.
+ */
+typedef struct rng_work_entry {
+ struct shw_queue_entry_t hdr; /*!< Standards SHW queue info. */
+ uint32_t length; /*!< Number of bytes still needed to satisfy request. */
+ uint32_t *data_local; /*!< Where data from RNG FIFO gets placed. */
+ uint8_t *data_user; /*!< Ultimate target of data. */
+ unsigned completed; /*!< Non-zero if job is done. */
+} rng_work_entry_t;
+
+/*****************************************************************************
+ * Function Prototypes
+ *****************************************************************************/
+
+#ifdef RNG_REGISTER_PEEK_POKE
+/*!
+ * Read value from an RNG register.
+ * The offset will be checked for validity as well as whether it is
+ * accessible at the time of the call.
+ *
+ * This routine cannot be used to read the RNG's Output FIFO if the RNG is in
+ * High Assurance mode.
+ *
+ * @param[in] register_offset The (byte) offset within the RNG block
+ * of the register to be queried. See
+ * RNG(A, C) registers for meanings.
+ * @param[out] value Pointer to where value from the register
+ * should be placed.
+ *
+ * @return See #rng_return_t.
+ */
+/* REQ-FSLSHW-PINTFC-API-LLF-001 */
+extern rng_return_t rng_read_register(uint32_t register_offset,
+ uint32_t * value);
+
+/*!
+ * Write a new value into an RNG register.
+ *
+ * The offset will be checked for validity as well as whether it is
+ * accessible at the time of the call.
+ *
+ * @param[in] register_offset The (byte) offset within the RNG block
+ * of the register to be modified. See
+ * RNG(A, C) registers for meanings.
+ * @param[in] value The value to store into the register.
+ *
+ * @return See #rng_return_t.
+ */
+/* REQ-FSLSHW-PINTFC-API-LLF-002 */
+extern rng_return_t rng_write_register(uint32_t register_offset,
+ uint32_t value);
+#endif /* RNG_REGISTER_PEEK_POKE */
+
+#endif /* RNG_DRIVER_H */
diff --git a/drivers/mxc/security/rng/include/rng_internals.h b/drivers/mxc/security/rng/include/rng_internals.h
new file mode 100644
index 000000000000..9f754a77e766
--- /dev/null
+++ b/drivers/mxc/security/rng/include/rng_internals.h
@@ -0,0 +1,680 @@
+/*
+ * Copyright (C) 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef RNG_INTERNALS_H
+#define RNG_INTERNALS_H
+
+/*! @file rng_internals.h
+ *
+ * This file contains definitions which are internal to the RNG driver.
+ *
+ * This header file should only ever be needed by rng_driver.c
+ *
+ * Compile-time flags minimally needed:
+ *
+ * @li Some sort of platform flag. (FSL_HAVE_RNGA or FSL_HAVE_RNGC)
+ *
+ * @ingroup RNG
+ */
+
+#include "portable_os.h"
+#include "shw_driver.h"
+#include "rng_driver.h"
+
+/*! @defgroup rngcompileflags RNG Compile Flags
+ *
+ * These are flags which are used to configure the RNG driver at compilation
+ * time.
+ *
+ * Most of them default to good values for normal operation, but some
+ * (#INT_RNG and #RNG_BASE_ADDR) need to be provided.
+ *
+ * The terms 'defined' and 'undefined' refer to whether a @c \#define (or -D on
+ * a compile command) has defined a given preprocessor symbol. If a given
+ * symbol is defined, then @c \#ifdef \<symbol\> will succeed. Some symbols
+ * described below default to not having a definition, i.e. they are undefined.
+ *
+ */
+
+/*! @addtogroup rngcompileflags */
+/*! @{ */
+
+/*!
+ * This is the maximum number of times the driver will loop waiting for the
+ * RNG hardware to say that it has generated random data. It prevents the
+ * driver from stalling forever should there be a hardware problem.
+ *
+ * Default value is 100. It should be revisited as CPU clocks speed up.
+ */
+#ifndef RNG_MAX_TRIES
+#define RNG_MAX_TRIES 100
+#endif
+
+/* Temporarily define compile-time flags to make Doxygen happy and allow them
+ to get into the documentation. */
+#ifdef DOXYGEN_HACK
+
+/*!
+ * This symbol is the base address of the RNG in the CPU memory map. It may
+ * come from some included header file, or it may come from the compile command
+ * line. This symbol has no default, and the driver will not compile without
+ * it.
+ */
+#define RNG_BASE_ADDR
+#undef RNG_BASE_ADDR
+
+/*!
+ * This symbol is the Interrupt Number of the RNG in the CPU. It may come
+ * from some included header file, or it may come from the compile command
+ * line. This symbol has no default, and the driver will not compile without
+ * it.
+ */
+#define INT_RNG
+#undef INT_RNG
+
+/*!
+ * Defining this symbol will allow other kernel programs to call the
+ * #rng_read_register() and #rng_write_register() functions. If this symbol is
+ * not defined, those functions will not be present in the driver.
+ */
+#define RNG_REGISTER_PEEK_POKE
+#undef RNG_REGISTER_PEEK_POKE
+
+/*!
+ * Turn on compilation of run-time operational, debug, and error messages.
+ *
+ * This flag is undefined by default.
+ */
+/* REQ-FSLSHW-DEBUG-001 */
+
+/*!
+ * Turn on compilation of run-time logging of access to the RNG registers,
+ * except for the RNG's Output FIFO register. See #RNG_ENTROPY_DEBUG.
+ *
+ * This flag is undefined by default
+ */
+#define RNG_REGISTER_DEBUG
+#undef RNG_REGISTER_DEBUG
+
+/*!
+ * Turn on compilation of run-time logging of reading of the RNG's Output FIFO
+ * register. This flag does nothing if #RNG_REGISTER_DEBUG is not defined.
+ *
+ * This flag is undefined by default
+ */
+#define RNG_ENTROPY_DEBUG
+#undef RNG_ENTROPY_DEBUG
+
+/*!
+ * If this flag is defined, the driver will not attempt to put the RNG into
+ * High Assurance mode.
+
+ * If it is undefined, the driver will attempt to put the RNG into High
+ * Assurance mode. If RNG fails to go into High Assurance mode, the driver
+ * will fail to initialize.
+
+ * In either case, if the RNG is already in this mode, the driver will operate
+ * normally.
+ *
+ * This flag is undefined by default.
+ */
+#define RNG_NO_FORCE_HIGH_ASSURANCE
+#undef RNG_NO_FORCE_HIGH_ASSURANCE
+
+/*!
+ * If this flag is defined, the driver will put the RNG into low power mode
+ * every opportunity.
+ *
+ * This flag is undefined by default.
+ */
+#define RNG_USE_LOW_POWER_MODE
+#undef RNG_USE_LOW_POWER_MODE
+
+/*! @} */
+#endif /* end DOXYGEN_HACK */
+
+/*!
+ * If this flag is defined, the driver will not attempt to put the RNG into
+ * High Assurance mode.
+
+ * If it is undefined, the driver will attempt to put the RNG into High
+ * Assurance mode. If RNG fails to go into High Assurance mode, the driver
+ * will fail to initialize.
+
+ * In either case, if the RNG is already in this mode, the driver will operate
+ * normally.
+ *
+ */
+#define RNG_NO_FORCE_HIGH_ASSURANCE
+
+/*!
+ * Read a 32-bit value from an RNG register. This macro depends upon
+ * #rng_base. The os_read32() macro operates on 32-bit quantities, as do
+ * all RNG register reads.
+ *
+ * @param offset Register byte offset within RNG.
+ *
+ * @return The value from the RNG's register.
+ */
+#ifndef RNG_REGISTER_DEBUG
+#define RNG_READ_REGISTER(offset) os_read32(rng_base+(offset))
+#else
+#define RNG_READ_REGISTER(offset) dbg_rng_read_register(offset)
+#endif
+
+/*!
+ * Write a 32-bit value to an RNG register. This macro depends upon
+ * #rng_base. The os_write32() macro operates on 32-bit quantities, as do
+ * all RNG register writes.
+ *
+ * @param offset Register byte offset within RNG.
+ * @param value 32-bit value to store into the register
+ *
+ * @return (void)
+ */
+#ifndef RNG_REGISTER_DEBUG
+#define RNG_WRITE_REGISTER(offset,value) \
+ (void)os_write32(rng_base+(offset), value)
+#else
+#define RNG_WRITE_REGISTER(offset,value) dbg_rng_write_register(offset,value)
+#endif
+
+#ifndef RNG_DRIVER_NAME
+/*! @addtogroup rngcompileflags */
+/*! @{ */
+/*! Name the driver will use to register itself to the kernel as the driver. */
+#define RNG_DRIVER_NAME "rng"
+/*! @} */
+#endif
+
+/*!
+ * Calculate number of words needed to hold the given number of bytes.
+ *
+ * @param byte_count Number of bytes
+ *
+ * @return Number of words
+ */
+#define BYTES_TO_WORDS(byte_count) \
+ (((byte_count)+sizeof(uint32_t)-1)/sizeof(uint32_t))
+
+/*! Gives high-level view of state of the RNG */
+typedef enum rng_status {
+ RNG_STATUS_INITIAL, /*!< Driver status before ever starting. */
+ RNG_STATUS_CHECKING, /*!< During driver initialization. */
+ RNG_STATUS_UNIMPLEMENTED, /*!< Hardware is non-existent / unreachable. */
+ RNG_STATUS_OK, /*!< Hardware is In Secure or Default state. */
+ RNG_STATUS_FAILED /*!< Hardware is In Failed state / other fatal
+ problem. Driver is still able to read/write
+ some registers, but cannot get Random
+ data. */
+} rng_status_t;
+
+static shw_queue_t rng_work_queue;
+
+/*****************************************************************************
+ *
+ * Function Declarations
+ *
+ *****************************************************************************/
+
+/* kernel interface functions */
+OS_DEV_INIT_DCL(rng_init);
+OS_DEV_TASK_DCL(rng_entropy_task);
+OS_DEV_SHUTDOWN_DCL(rng_shutdown);
+OS_DEV_ISR_DCL(rng_irq);
+
+#define RNG_ADD_QUEUE_ENTRY(pool, entry) \
+ SHW_ADD_QUEUE_ENTRY(pool, (shw_queue_entry_t*)entry)
+
+#define RNG_REMOVE_QUEUE_ENTRY(pool, entry) \
+ SHW_REMOVE_QUEUE_ENTRY(pool, (shw_queue_entry_t*)entry)
+#define RNG_GET_WORK_ENTRY() \
+ (rng_work_entry_t*)SHW_POP_FIRST_ENTRY(&rng_work_queue)
+
+/*!
+ * Add an work item to a work list. Item will be marked incomplete.
+ *
+ * @param work Work entry to place at tail of list.
+ *
+ * @return none
+ */
+inline static void RNG_ADD_WORK_ENTRY(rng_work_entry_t * work)
+{
+ work->completed = FALSE;
+
+ SHW_ADD_QUEUE_ENTRY(&rng_work_queue, (shw_queue_entry_t *) work);
+
+ os_dev_schedule_task(rng_entropy_task);
+}
+
+/*!
+ * For #rng_check_register_accessible(), check read permission on given
+ * register.
+ */
+#define RNG_CHECK_READ 0
+
+/*!
+ * For #rng_check_register_accessible(), check write permission on given
+ * register.
+ */
+#define RNG_CHECK_WRITE 1
+
+/* Define different helper symbols based on RNG type */
+#ifdef FSL_HAVE_RNGA
+
+/******************************************************************************
+ *
+ * RNGA support
+ *
+ *****************************************************************************/
+
+/*! Interrupt number for driver. */
+#if defined(MXC_INT_RNG)
+/* Most modern definition */
+#define INT_RNG MXC_INT_RNG
+#elif defined(MXC_INT_RNGA)
+#define INT_RNG MXC_INT_RNGA
+#else
+#define INT_RNG INT_RNGA
+#endif
+
+/*! Base (bus?) address of RNG component. */
+#define RNG_BASE_ADDR RNGA_BASE_ADDR
+
+/*! Read and return the status register. */
+#define RNG_GET_STATUS() \
+ RNG_READ_REGISTER(RNGA_STATUS)
+/*! Configure RNG for Auto seeding */
+#define RNG_AUTO_SEED()
+/* Put RNG for Seed Generation */
+#define RNG_SEED_GEN()
+/*!
+ * Return RNG Type value. Should be RNG_TYPE_RNGA, RNG_TYPE_RNGB,
+ * or RNG_TYPE_RNGC.
+ */
+#define RNG_GET_RNG_TYPE() \
+ ((RNG_READ_REGISTER(RNGA_CONTROL) & RNGA_CONTROL_RNG_TYPE_MASK) \
+ >> RNGA_CONTROL_RNG_TYPE_SHIFT)
+
+/*!
+ * Verify Type value of RNG.
+ *
+ * Returns true of OK, false if not.
+ */
+#define RNG_VERIFY_TYPE(type) \
+ ((type) == RNG_TYPE_RNGA)
+
+/*! Returns non-zero if RNG device is reporting an error. */
+#define RNG_HAS_ERROR() \
+ (RNG_READ_REGISTER(RNGA_STATUS) & RNGA_STATUS_ERROR_INTERRUPT)
+/*! Returns non-zero if Bad Key is selected */
+#define RNG_HAS_BAD_KEY() 0
+/*! Return non-zero if Self Test Done */
+#define RNG_SELF_TEST_DONE() 0
+/*! Returns non-zero if RNG ring oscillators have failed. */
+#define RNG_OSCILLATOR_FAILED() \
+ (RNG_READ_REGISTER(RNGA_STATUS) & RNGA_STATUS_OSCILLATOR_DEAD)
+
+/*! Returns maximum number of 32-bit words in the RNG's output fifo. */
+#define RNG_GET_FIFO_SIZE() \
+ ((RNG_READ_REGISTER(RNGA_STATUS) & RNGA_STATUS_OUTPUT_FIFO_SIZE_MASK) \
+ >> RNGA_STATUS_OUTPUT_FIFO_SIZE_SHIFT)
+
+/*! Returns number of 32-bit words currently in the RNG's output fifo. */
+#define RNG_GET_WORDS_IN_FIFO() \
+ ((RNG_READ_REGISTER(RNGA_STATUS) & RNGA_STATUS_OUTPUT_FIFO_LEVEL_MASK) \
+ >> RNGA_STATUS_OUTPUT_FIFO_LEVEL_SHIFT)
+/* Configuring RNG for Self Test */
+#define RNG_SELF_TEST()
+/*! Get a random value from the RNG's output FIFO. */
+#define RNG_READ_FIFO() \
+ RNG_READ_REGISTER(RNGA_OUTPUT_FIFO)
+
+/*! Put entropy into the RNG's algorithm.
+ * @param value 32-bit value to add to RNG's entropy.
+ **/
+#define RNG_ADD_ENTROPY(value) \
+ RNG_WRITE_REGISTER(RNGA_ENTROPY, (value))
+/*! Return non-zero in case of Error during Self Test */
+#define RNG_CHECK_SELF_ERR() 0
+/*! Return non-zero in case of Error during Seed Generation */
+#define RNG_CHECK_SEED_ERR() 0
+/*! Get the RNG started at generating output. */
+#define RNG_GO() \
+{ \
+ register uint32_t control = RNG_READ_REGISTER(RNGA_CONTROL); \
+ RNG_WRITE_REGISTER(RNGA_CONTROL, control | RNGA_CONTROL_GO); \
+}
+/*! To clear all Error Bits in Error Status Register */
+#define RNG_CLEAR_ERR()
+/*! Put RNG into High Assurance mode */
+#define RNG_SET_HIGH_ASSURANCE() \
+{ \
+ register uint32_t control = RNG_READ_REGISTER(RNGA_CONTROL); \
+ RNG_WRITE_REGISTER(RNGA_CONTROL, control | RNGA_CONTROL_HIGH_ASSURANCE); \
+}
+
+/*! Return non-zero if the RNG is in High Assurance mode. */
+#define RNG_GET_HIGH_ASSURANCE() \
+ (RNG_READ_REGISTER(RNGA_CONTROL) & RNGA_CONTROL_HIGH_ASSURANCE)
+
+/*! Clear all status, error and otherwise. */
+#define RNG_CLEAR_ALL_STATUS() \
+{ \
+ register uint32_t control = RNG_READ_REGISTER(RNGA_CONTROL); \
+ RNG_WRITE_REGISTER(RNGA_CONTROL, control | RNGA_CONTROL_CLEAR_INTERRUPT); \
+}
+/* Return non-zero if RESEED Required */
+#define RNG_RESEED() 1
+
+/*! Return non-zero if Seeding is done */
+#define RNG_SEED_DONE() 1
+
+/*! Return non-zero if everything seems OK with the RNG. */
+#define RNG_WORKING() \
+ ((RNG_READ_REGISTER(RNGA_STATUS) \
+ & (RNGA_STATUS_SLEEP | RNGA_STATUS_SECURITY_VIOLATION \
+ | RNGA_STATUS_ERROR_INTERRUPT | RNGA_STATUS_FIFO_UNDERFLOW \
+ | RNGA_STATUS_LAST_READ_STATUS )) == 0)
+
+/*! Put the RNG into sleep (low-power) mode. */
+#define RNG_SLEEP() \
+{ \
+ register uint32_t control = RNG_READ_REGISTER(RNGA_CONTROL); \
+ RNG_WRITE_REGISTER(RNGA_CONTROL, control | RNGA_CONTROL_SLEEP); \
+}
+
+/*! Wake the RNG from sleep (low-power) mode. */
+#define RNG_WAKE() \
+{ \
+ uint32_t control = RNG_READ_REGISTER(RNGA_CONTROL); \
+ RNG_WRITE_REGISTER(RNGA_CONTROL, control & ~RNGA_CONTROL_SLEEP); \
+}
+
+/*! Mask interrupts so that the driver/OS will not see them. */
+#define RNG_MASK_ALL_INTERRUPTS() \
+{ \
+ register uint32_t control = RNG_READ_REGISTER(RNGA_CONTROL); \
+ RNG_WRITE_REGISTER(RNGA_CONTROL, control | RNGA_CONTROL_MASK_INTERRUPTS); \
+}
+
+/*! Unmask interrupts so that the driver/OS will see them. */
+#define RNG_UNMASK_ALL_INTERRUPTS() \
+{ \
+ register uint32_t control = RNG_READ_REGISTER(RNGA_CONTROL); \
+ RNG_WRITE_REGISTER(RNGA_CONTROL, control & ~RNGA_CONTROL_MASK_INTERRUPTS);\
+}
+
+/*!
+ * @def RNG_PUT_RNG_TO_SLEEP()
+ *
+ * If compiled with #RNG_USE_LOW_POWER_MODE, this routine will put the RNG
+ * to sleep (low power mode).
+ *
+ * @return none
+ */
+/*!
+ * @def RNG_WAKE_RNG_FROM_SLEEP()
+ *
+ * If compiled with #RNG_USE_LOW_POWER_MODE, this routine will wake the RNG
+ * from sleep (low power mode).
+ *
+ * @return none
+ */
+#ifdef RNG_USE_LOW_POWER_MODE
+
+#define RNG_PUT_RNG_TO_SLEEP() \
+ RNG_SLEEP()
+
+#define RNG_WAKE_FROM_SLEEP() \
+ RNG_WAKE() 1
+
+#else /* not low power mode */
+
+#define RNG_PUT_RNG_TO_SLEEP()
+
+#define RNG_WAKE_FROM_SLEEP()
+
+#endif /* Use low-power mode */
+
+#else /* FSL_HAVE_RNGB or FSL_HAVE_RNGC */
+
+/******************************************************************************
+ *
+ * RNGB and RNGC support
+ *
+ *****************************************************************************/
+/*
+ * The operational interfaces for RNGB and RNGC are almost identical, so
+ * the defines for RNGC work fine for both. There are minor differences
+ * which will be treated within this conditional block.
+ */
+
+/*! Interrupt number for driver. */
+#if defined(MXC_INT_RNG)
+/* Most modern definition */
+#define INT_RNG MXC_INT_RNG
+#elif defined(MXC_INT_RNGC)
+#define INT_RNG MXC_INT_RNGC
+#elif defined(MXC_INT_RNGB)
+#define INT_RNG MXC_INT_RNGB
+#elif defined(INT_RNGC)
+#define INT_RNG INT_RNGC
+#else
+#error NO_INTERRUPT_DEFINED
+#endif
+
+/*! Base address of RNG component. */
+#ifdef FSL_HAVE_RNGB
+#define RNG_BASE_ADDR RNGB_BASE_ADDR
+#else
+#define RNG_BASE_ADDR RNGC_BASE_ADDR
+#endif
+
+/*! Read and return the status register. */
+#define RNG_GET_STATUS() \
+ RNG_READ_REGISTER(RNGC_ERROR)
+
+/*!
+ * Return RNG Type value. Should be RNG_TYPE_RNGA or RNG_TYPE_RNGC.
+ */
+#define RNG_GET_RNG_TYPE() \
+ ((RNG_READ_REGISTER(RNGC_VERSION_ID) & RNGC_VERID_RNG_TYPE_MASK) \
+ >> RNGC_VERID_RNG_TYPE_SHIFT)
+
+/*!
+ * Verify Type value of RNG.
+ *
+ * Returns true of OK, false if not.
+ */
+#ifdef FSL_HAVE_RNGB
+#define RNG_VERIFY_TYPE(type) \
+ ((type) == RNG_TYPE_RNGB)
+#else /* RNGC */
+#define RNG_VERIFY_TYPE(type) \
+ ((type) == RNG_TYPE_RNGC)
+#endif
+
+/*! Returns non-zero if RNG device is reporting an error. */
+#define RNG_HAS_ERROR() \
+ (RNG_READ_REGISTER(RNGC_STATUS) & RNGC_STATUS_ERROR)
+/*! Returns non-zero if Bad Key is selected */
+#define RNG_HAS_BAD_KEY() \
+ (RNG_READ_REGISTER(RNGC_ERROR) & RNGC_ERROR_STATUS_BAD_KEY)
+/*! Returns non-zero if RNG ring oscillators have failed. */
+#define RNG_OSCILLATOR_FAILED() \
+ (RNG_READ_REGISTER(RNGC_ERROR) & RNGC_ERROR_STATUS_OSC_ERR)
+
+/*! Returns maximum number of 32-bit words in the RNG's output fifo. */
+#define RNG_GET_FIFO_SIZE() \
+ ((RNG_READ_REGISTER(RNGC_STATUS) & RNGC_STATUS_FIFO_SIZE_MASK) \
+ >> RNGC_STATUS_FIFO_SIZE_SHIFT)
+
+/*! Returns number of 32-bit words currently in the RNG's output fifo. */
+#define RNG_GET_WORDS_IN_FIFO() \
+ ((RNG_READ_REGISTER(RNGC_STATUS) & RNGC_STATUS_FIFO_LEVEL_MASK) \
+ >> RNGC_STATUS_FIFO_LEVEL_SHIFT)
+
+/*! Get a random value from the RNG's output FIFO. */
+#define RNG_READ_FIFO() \
+ RNG_READ_REGISTER(RNGC_FIFO)
+
+/*! Put entropy into the RNG's algorithm.
+ * @param value 32-bit value to add to RNG's entropy.
+ **/
+#ifdef FSL_HAVE_RNGB
+#define RNG_ADD_ENTROPY(value) \
+ RNG_WRITE_REGISTER(RNGB_ENTROPY, value)
+#else /* RNGC does not have Entropy register */
+#define RNG_ADD_ENTROPY(value)
+#endif
+/*! Wake the RNG from sleep (low-power) mode. */
+#define RNG_WAKE() 1
+/*! Get the RNG started at generating output. */
+#define RNG_GO()
+/*! Put RNG into High Assurance mode. */
+#define RNG_SET_HIGH_ASSURANCE()
+/*! Returns non-zero in case of Error during Self Test */
+#define RNG_CHECK_SELF_ERR() \
+ (RNG_READ_REGISTER(RNGC_ERROR) & RNGC_ERROR_STATUS_ST_ERR)
+/*! Return non-zero in case of Error during Seed Generation */
+#define RNG_CHECK_SEED_ERR() \
+ (RNG_READ_REGISTER(RNGC_ERROR) & RNGC_ERROR_STATUS_STAT_ERR)
+
+/*! Configure RNG for Self Test */
+#define RNG_SELF_TEST() \
+{ \
+ register uint32_t command = RNG_READ_REGISTER(RNGC_COMMAND); \
+ RNG_WRITE_REGISTER(RNGC_COMMAND, command \
+ | RNGC_COMMAND_SELF_TEST); \
+}
+/*! Clearing the Error bits in Error Status Register */
+#define RNG_CLEAR_ERR() \
+{ \
+ register uint32_t command = RNG_READ_REGISTER(RNGC_COMMAND); \
+ RNG_WRITE_REGISTER(RNGC_COMMAND, command \
+ | RNGC_COMMAND_CLEAR_ERROR); \
+}
+
+/*! Return non-zero if Self Test Done */
+#define RNG_SELF_TEST_DONE() \
+ (RNG_READ_REGISTER(RNGC_STATUS) & RNGC_STATUS_ST_DONE)
+/* Put RNG for SEED Generation */
+#define RNG_SEED_GEN() \
+{ \
+ register uint32_t command = RNG_READ_REGISTER(RNGC_COMMAND); \
+ RNG_WRITE_REGISTER(RNGC_COMMAND, command \
+ | RNGC_COMMAND_SEED); \
+}
+/* Return non-zero if RESEED Required */
+#define RNG_RESEED() \
+ (RNG_READ_REGISTER(RNGC_STATUS) & RNGC_STATUS_RESEED)
+
+/*! Return non-zero if the RNG is in High Assurance mode. */
+#define RNG_GET_HIGH_ASSURANCE() (RNG_READ_REGISTER(RNGC_STATUS) & \
+ RNGC_STATUS_SEC_STATE)
+
+/*! Clear all status, error and otherwise. */
+#define RNG_CLEAR_ALL_STATUS() \
+ RNG_WRITE_REGISTER(RNGC_COMMAND, \
+ RNGC_COMMAND_CLEAR_INTERRUPT \
+ | RNGC_COMMAND_CLEAR_ERROR)
+
+/*! Return non-zero if everything seems OK with the RNG. */
+#define RNG_WORKING() \
+ ((RNG_READ_REGISTER(RNGC_ERROR) \
+ & (RNGC_ERROR_STATUS_STAT_ERR | RNGC_ERROR_STATUS_RAND_ERR \
+ | RNGC_ERROR_STATUS_FIFO_ERR | RNGC_ERROR_STATUS_ST_ERR | \
+ RNGC_ERROR_STATUS_OSC_ERR | RNGC_ERROR_STATUS_LFSR_ERR )) == 0)
+/*! Return Non zero if SEEDING is DONE */
+#define RNG_SEED_DONE() \
+ ((RNG_READ_REGISTER(RNGC_STATUS) & RNGC_STATUS_SEED_DONE) != 0)
+
+/*! Put the RNG into sleep (low-power) mode. */
+#define RNG_SLEEP()
+
+/*! Wake the RNG from sleep (low-power) mode. */
+
+/*! Mask interrupts so that the driver/OS will not see them. */
+#define RNG_MASK_ALL_INTERRUPTS() \
+{ \
+ register uint32_t control = RNG_READ_REGISTER(RNGC_CONTROL); \
+ RNG_WRITE_REGISTER(RNGC_CONTROL, control \
+ | RNGC_CONTROL_MASK_DONE \
+ | RNGC_CONTROL_MASK_ERROR); \
+}
+/*! Configuring RNGC for self Test. */
+
+#define RNG_AUTO_SEED() \
+{ \
+ register uint32_t control = RNG_READ_REGISTER(RNGC_CONTROL); \
+ RNG_WRITE_REGISTER(RNGC_CONTROL, control \
+ | RNGC_CONTROL_AUTO_SEED); \
+}
+
+/*! Unmask interrupts so that the driver/OS will see them. */
+#define RNG_UNMASK_ALL_INTERRUPTS() \
+{ \
+ register uint32_t control = RNG_READ_REGISTER(RNGC_CONTROL); \
+ RNG_WRITE_REGISTER(RNGC_CONTROL, \
+ control & ~(RNGC_CONTROL_MASK_DONE|RNGC_CONTROL_MASK_ERROR)); \
+}
+
+/*! Put RNG to sleep if appropriate. */
+#define RNG_PUT_RNG_TO_SLEEP()
+
+/*! Wake RNG from sleep if necessary. */
+#define RNG_WAKE_FROM_SLEEP()
+
+#endif /* RNG TYPE */
+
+/* internal functions */
+static os_error_code rng_map_RNG_memory(void);
+static os_error_code rng_setup_interrupt_handling(void);
+#ifdef RNG_REGISTER_PEEK_POKE
+inline static int rng_check_register_offset(uint32_t offset);
+inline static int rng_check_register_accessible(uint32_t offset,
+ int access_write);
+#endif /* DEBUG_RNG_REGISTERS */
+static fsl_shw_return_t rng_drain_fifo(uint32_t * random_p, int count_words);
+static os_error_code rng_grab_config_values(void);
+static void rng_cleanup(void);
+
+#ifdef FSL_HAVE_RNGA
+static void rng_sec_failure(void);
+#endif
+
+#ifdef RNG_REGISTER_DEBUG
+static uint32_t dbg_rng_read_register(uint32_t offset);
+static void dbg_rng_write_register(uint32_t offset, uint32_t value);
+#endif
+
+#if defined(LINUX_VERSION_CODE)
+
+EXPORT_SYMBOL(fsl_shw_add_entropy);
+EXPORT_SYMBOL(fsl_shw_get_random);
+
+#ifdef RNG_REGISTER_PEEK_POKE
+/* For Linux kernel, export the API functions to other kernel modules */
+EXPORT_SYMBOL(rng_read_register);
+EXPORT_SYMBOL(rng_write_register);
+#endif /* DEBUG_RNG_REGISTERS */
+
+
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("Device Driver for RNG");
+
+#endif /* LINUX_VERSION_CODE */
+
+#endif /* RNG_INTERNALS_H */
diff --git a/drivers/mxc/security/rng/include/rng_rnga.h b/drivers/mxc/security/rng/include/rng_rnga.h
new file mode 100644
index 000000000000..a3a86b961506
--- /dev/null
+++ b/drivers/mxc/security/rng/include/rng_rnga.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef RNG_RNGA_H
+#define RNG_RNGA_H
+
+/*! @defgroup rngaregs RNGA Registers
+ * @ingroup RNG
+ * These are the definitions for the RNG registers and their offsets
+ * within the RNG. They are used in the @c register_offset parameter of
+ * #rng_read_register() and #rng_write_register().
+ */
+/*! @addtogroup rngaregs */
+/*! @{ */
+
+/*! Control Register. See @ref rngacontrolreg. */
+#define RNGA_CONTROL 0x00
+/*! Status Register. See @ref rngastatusreg. */
+#define RNGA_STATUS 0x04
+/*! Register for adding to the Entropy of the RNG */
+#define RNGA_ENTROPY 0x08
+/*! Register containing latest 32 bits of random value */
+#define RNGA_OUTPUT_FIFO 0x0c
+/*! Mode Register. Non-secure mode access only. See @ref rngmodereg. */
+#define RNGA_MODE 0x10
+/*! Verification Control Register. Non-secure mode access only. See
+ * @ref rngvfctlreg. */
+#define RNGA_VERIFICATION_CONTROL 0x14
+/*! Oscillator Control Counter Register. Non-secure mode access only.
+ * See @ref rngosccntctlreg. */
+#define RNGA_OSCILLATOR_CONTROL_COUNTER 0x18
+/*! Oscillator 1 Counter Register. Non-secure mode access only. See
+ * @ref rngosccntreg. */
+#define RNGA_OSCILLATOR1_COUNTER 0x1c
+/*! Oscillator 2 Counter Register. Non-secure mode access only. See
+ * @ref rngosccntreg. */
+#define RNGA_OSCILLATOR2_COUNTER 0x20
+/*! Oscillator Counter Status Register. Non-secure mode access only. See
+ * @ref rngosccntstatreg. */
+#define RNGA_OSCILLATOR_COUNTER_STATUS 0x24
+/*! @} */
+
+/*! Total address space of the RNGA, in bytes */
+#define RNG_ADDRESS_RANGE 0x28
+
+/*! @defgroup rngacontrolreg RNGA Control Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngacontrolreg */
+/*! @{ */
+/*! These bits are unimplemented or reserved */
+#define RNGA_CONTROL_ZEROS_MASK 0x0fffffe0
+/*! 'RNG type' - should be 0 for RNGA */
+#define RNGA_CONTROL_RNG_TYPE_MASK 0xf0000000
+/*! Number of bits to shift the type to get it to LSB */
+#define RNGA_CONTROL_RNG_TYPE_SHIFT 28
+/*! Put RNG to sleep */
+#define RNGA_CONTROL_SLEEP 0x00000010
+/*! Clear interrupt & status */
+#define RNGA_CONTROL_CLEAR_INTERRUPT 0x00000008
+/*! Mask interrupt generation */
+#define RNGA_CONTROL_MASK_INTERRUPTS 0x00000004
+/*! Enter into Secure Mode. Notify SCC of security violation should FIFO
+ * underflow occur. */
+#define RNGA_CONTROL_HIGH_ASSURANCE 0x00000002
+/*! Load data into FIFO */
+#define RNGA_CONTROL_GO 0x00000001
+/*! @} */
+
+/*! @defgroup rngastatusreg RNGA Status Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngastatusreg */
+/*! @{ */
+/*! RNG Oscillator not working */
+#define RNGA_STATUS_OSCILLATOR_DEAD 0x80000000
+/*! These bits are undefined or reserved */
+#define RNGA_STATUS_ZEROS1_MASK 0x7f000000
+/*! How big FIFO is, in bytes */
+#define RNGA_STATUS_OUTPUT_FIFO_SIZE_MASK 0x00ff0000
+/*! How many bits right to shift fifo size to make it LSB */
+#define RNGA_STATUS_OUTPUT_FIFO_SIZE_SHIFT 16
+/*! How many bytes are currently in the FIFO */
+#define RNGA_STATUS_OUTPUT_FIFO_LEVEL_MASK 0x0000ff00
+/*! How many bits right to shift fifo level to make it LSB */
+#define RNGA_STATUS_OUTPUT_FIFO_LEVEL_SHIFT 8
+/*! These bits are undefined or reserved. */
+#define RNGA_STATUS_ZEROS2_MASK 0x000000e0
+/*! RNG is sleeping. */
+#define RNGA_STATUS_SLEEP 0x00000010
+/*! Error detected. */
+#define RNGA_STATUS_ERROR_INTERRUPT 0x00000008
+/*! FIFO was empty on some read since last status read. */
+#define RNGA_STATUS_FIFO_UNDERFLOW 0x00000004
+/*! FIFO was empty on most recent read. */
+#define RNGA_STATUS_LAST_READ_STATUS 0x00000002
+/*! Security violation occurred. Will only happen in High Assurance mode. */
+#define RNGA_STATUS_SECURITY_VIOLATION 0x00000001
+/*! @} */
+
+/*! @defgroup rngmodereg RNG Mode Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngmodereg */
+/*! @{ */
+/*! These bits are undefined or reserved */
+#define RNGA_MODE_ZEROS_MASK 0xfffffffc
+/*! RNG is in / put RNG in Oscillator Frequency Test Mode. */
+#define RNGA_MODE_OSCILLATOR_FREQ_TEST 0x00000002
+/*! Put RNG in verification mode / RNG is in verification mode. */
+#define RNGA_MODE_VERIFICATION 0x00000001
+/*! @} */
+
+/*! @defgroup rngvfctlreg RNG Verification Control Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngvfctlreg */
+/*! @{ */
+/*! These bits are undefined or reserved. */
+#define RNGA_VFCTL_ZEROS_MASK 0xfffffff8
+/*! Reset the shift registers. */
+#define RNGA_VFCTL_RESET_SHIFT_REGISTERS 0x00000004
+/*! Drive shift registers from system clock. */
+#define RNGA_VFCTL_FORCE_SYSTEM_CLOCK 0x00000002
+/*! Turn off shift register clocks. */
+#define RNGA_VFCTL_SHIFT_CLOCK_OFF 0x00000001
+/*! @} */
+
+/*!
+ * @defgroup rngosccntctlreg RNG Oscillator Counter Control Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngosccntctlreg */
+/*! @{ */
+/*! These bits are undefined or reserved. */
+#define RNGA_OSCCR_ZEROS_MASK 0xfffc0000
+/*! Bits containing clock cycle counter */
+#define RNGA_OSCCR_CLOCK_CYCLES_MASK 0x0003ffff
+/*! Bits to shift right RNG_OSCCR_CLOCK_CYCLES_MASK */
+#define RNGA_OSCCR_CLOCK_CYCLES_SHIFT 0
+/*! @} */
+
+/*!
+ * @defgroup rngosccntreg RNG Oscillator (1 and 2) Counter Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngosccntreg */
+/*! @{ */
+/*! These bits are undefined or reserved. */
+#define RNGA_COUNTER_ZEROS_MASK 0xfff00000
+/*! Bits containing number of clock pulses received from the oscillator. */
+#define RNGA_COUNTER_PULSES_MASK 0x000fffff
+/*! Bits right to shift RNG_COUNTER_PULSES_MASK to make it LSB. */
+#define RNGA_COUNTER_PULSES_SHIFT 0
+/*! @} */
+
+/*!
+ * @defgroup rngosccntstatreg RNG Oscillator Counter Status Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngosccntstatreg */
+/*! @{ */
+/*! These bits are undefined or reserved. */
+#define RNGA_COUNTER_STATUS_ZEROS_MASK 0xfffffffc
+/*! Oscillator 2 has toggled 0x400 times */
+#define RNGA_COUNTER_STATUS_OSCILLATOR2 0x00000002
+/*! Oscillator 1 has toggled 0x400 times */
+#define RNGA_COUNTER_STATUS_OSCILLATOR1 0x00000001
+/*! @} */
+
+#endif /* RNG_RNGA_H */
diff --git a/drivers/mxc/security/rng/include/rng_rngc.h b/drivers/mxc/security/rng/include/rng_rngc.h
new file mode 100644
index 000000000000..d7b7f127f74c
--- /dev/null
+++ b/drivers/mxc/security/rng/include/rng_rngc.h
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+/*!
+ * @file rng_rngc.h
+ *
+ * Definition of the registers for the RNGB and RNGC. The names start with
+ * RNGC where they are in common or relate only to the RNGC; the RNGB-only
+ * definitions begin with RNGB.
+ *
+ */
+
+#ifndef RNG_RNGC_H
+#define RNG_RNGC_H
+
+#define RNGC_VERSION_MAJOR3 3
+
+/*! @defgroup rngcregs RNGB/RNGC Registers
+ * These are the definitions for the RNG registers and their offsets
+ * within the RNG. They are used in the @c register_offset parameter of
+ * #rng_read_register() and #rng_write_register().
+ *
+ * @ingroup RNG
+ */
+/*! @addtogroup rngcregs */
+/*! @{ */
+
+/*! RNGC Version ID Register R/W */
+#define RNGC_VERSION_ID 0x0000
+/*! RNGC Command Register R/W */
+#define RNGC_COMMAND 0x0004
+/*! RNGC Control Register R/W */
+#define RNGC_CONTROL 0x0008
+/*! RNGC Status Register R */
+#define RNGC_STATUS 0x000C
+/*! RNGC Error Status Register R */
+#define RNGC_ERROR 0x0010
+/*! RNGC FIFO Register W */
+#define RNGC_FIFO 0x0014
+/*! Undefined */
+#define RNGC_UNDEF_18 0x0018
+/*! RNGB Entropy Register W */
+#define RNGB_ENTROPY 0x0018
+/*! Undefined */
+#define RNGC_UNDEF_1C 0x001C
+/*! RNGC Verification Control Register1 R/W */
+#define RNGC_VERIFICATION_CONTROL 0x0020
+/*! Undefined */
+#define RNGC_UNDEF_24 0x0024
+/*! RNGB XKEY Data Register R */
+#define RNGB_XKEY 0x0024
+/*! RNGC Oscillator Counter Control Register1 R/W */
+#define RNGC_OSC_COUNTER_CONTROL 0x0028
+/*! RNGC Oscillator Counter Register1 R */
+#define RNGC_OSC_COUNTER 0x002C
+/*! RNGC Oscillator Counter Status Register1 R */
+#define RNGC_OSC_COUNTER_STATUS 0x0030
+/*! @} */
+
+/*! @defgroup rngcveridreg RNGB/RNGC Version ID Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngcveridreg */
+/*! @{ */
+/*! These bits are unimplemented or reserved */
+#define RNGC_VERID_ZEROS_MASK 0x0f000000
+/*! Mask for RNG TYPE */
+#define RNGC_VERID_RNG_TYPE_MASK 0xf0000000
+/*! Shift to make RNG TYPE be LSB */
+#define RNGC_VERID_RNG_TYPE_SHIFT 28
+/*! Mask for RNG Chip Version */
+#define RNGC_VERID_CHIP_VERSION_MASK 0x00ff0000
+/*! Shift to make RNG Chip version be LSB */
+#define RNGC_VERID_CHIP_VERSION_SHIFT 16
+/*! Mask for RNG Major Version */
+#define RNGC_VERID_VERSION_MAJOR_MASK 0x0000ff00
+/*! Shift to make RNG Major version be LSB */
+#define RNGC_VERID_VERSION_MAJOR_SHIFT 8
+/*! Mask for RNG Minor Version */
+#define RNGC_VERID_VERSION_MINOR_MASK 0x000000ff
+/*! Shift to make RNG Minor version be LSB */
+#define RNGC_VERID_VERSION_MINOR_SHIFT 0
+/*! @} */
+
+/*! @defgroup rngccommandreg RNGB/RNGC Command Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngccommandreg */
+/*! @{ */
+/*! These bits are unimplemented or reserved. */
+#define RNGC_COMMAND_ZEROS_MASK 0xffffff8c
+/*! Perform a software reset of the RNGC. */
+#define RNGC_COMMAND_SOFTWARE_RESET 0x00000040
+/*! Clear error from Error Status register (and interrupt). */
+#define RNGC_COMMAND_CLEAR_ERROR 0x00000020
+/*! Clear interrupt & status. */
+#define RNGC_COMMAND_CLEAR_INTERRUPT 0x00000010
+/*! Start RNGC seed generation. */
+#define RNGC_COMMAND_SEED 0x00000002
+/*! Perform a self test of (and reset) the RNGC. */
+#define RNGC_COMMAND_SELF_TEST 0x00000001
+/*! @} */
+
+/*! @defgroup rngccontrolreg RNGB/RNGC Control Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngccontrolreg */
+/*! @{ */
+/*! These bits are unimplemented or reserved */
+#define RNGC_CONTROL_ZEROS_MASK 0xfffffc8c
+/*! Allow access to verification registers. */
+#define RNGC_CONTROL_CTL_ACC 0x00000200
+/*! Put RNGC into deterministic verifcation mode. */
+#define RNGC_CONTROL_VERIF_MODE 0x00000100
+/*! Prevent RNGC from generating interrupts caused by errors. */
+#define RNGC_CONTROL_MASK_ERROR 0x00000040
+
+/*!
+ * Prevent RNGB/RNGC from generating interrupts after Seed Done or Self Test
+ * Mode completion.
+ */
+#define RNGC_CONTROL_MASK_DONE 0x00000020
+/*! Allow RNGC to generate a new seed whenever it is needed. */
+#define RNGC_CONTROL_AUTO_SEED 0x00000010
+/*! Set FIFO Underflow Response.*/
+#define RNGC_CONTROL_FIFO_UFLOW_MASK 0x00000003
+/*! Shift value to make FIFO Underflow Response be LSB. */
+#define RNGC_CONTROL_FIFO_UFLOW_SHIFT 0
+
+/*! @} */
+
+/*! @{ */
+/*! FIFO Underflow should cause ... */
+#define RNGC_CONTROL_FIFO_UFLOW_ZEROS_ERROR 0
+/*! FIFO Underflow should cause ... */
+#define RNGC_CONTROL_FIFO_UFLOW_ZEROS_ERROR2 1
+/*! FIFO Underflow should cause ... */
+#define RNGC_CONTROL_FIFO_UFLOW_BUS_XFR 2
+/*! FIFO Underflow should cause ... */
+#define RNGC_CONTROL_FIFO_UFLOW_ZEROS_INTR 3
+/*! @} */
+
+/*! @defgroup rngcstatusreg RNGB/RNGC Status Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngcstatusreg */
+/*! @{ */
+/*! Unused or MBZ. */
+#define RNGC_STATUS_ZEROS_MASK 0x003e0080
+/*!
+ * Statistical tests pass-fail. Individual bits on indicate failure of a
+ * particular test.
+ */
+#define RNGC_STATUS_STAT_TEST_PF_MASK 0xff000000
+/*! Mask to get Statistical PF to be LSB. */
+#define RNGC_STATUS_STAT_TEST_PF_SHIFT 24
+/*!
+ * Self tests pass-fail. Individual bits on indicate failure of a
+ * particular test.
+ */
+#define RNGC_STATUS_ST_PF_MASK 0x00c00000
+/*! Shift value to get Self Test PF field to be LSB. */
+#define RNGC_STATUS_ST_PF_SHIFT 22
+/* TRNG Self test pass-fail */
+#define RNGC_STATUS_ST_PF_TRNG 0x00800000
+/* PRNG Self test pass-fail */
+#define RNGC_STATUS_ST_PF_PRNG 0x00400000
+/*! Error detected in RNGC. See Error Status register. */
+#define RNGC_STATUS_ERROR 0x00010000
+/*! Size of the internal FIFO in 32-bit words. */
+#define RNGC_STATUS_FIFO_SIZE_MASK 0x0000f000
+/*! Shift value to get FIFO Size to be LSB. */
+#define RNGC_STATUS_FIFO_SIZE_SHIFT 12
+/*! The level (available data) of the internal FIFO in 32-bit words. */
+#define RNGC_STATUS_FIFO_LEVEL_MASK 0x00000f00
+/*! Shift value to get FIFO Level to be LSB. */
+#define RNGC_STATUS_FIFO_LEVEL_SHIFT 8
+/*! A new seed is ready for use. */
+#define RNGC_STATUS_NEXT_SEED_DONE 0x00000040
+/*! The first seed has been generated. */
+#define RNGC_STATUS_SEED_DONE 0x00000020
+/*! Self Test has been completed. */
+#define RNGC_STATUS_ST_DONE 0x00000010
+/*! Reseed is necessary. */
+#define RNGC_STATUS_RESEED 0x00000008
+/*! RNGC is sleeping. */
+#define RNGC_STATUS_SLEEP 0x00000004
+/*! RNGC is currently generating numbers, seeding, generating next seed, or
+ performing a self test. */
+#define RNGC_STATUS_BUSY 0x00000002
+/*! RNGC is in secure state. */
+#define RNGC_STATUS_SEC_STATE 0x00000001
+
+/*! @} */
+
+/*! @defgroup rngcerrstatusreg RNGB/RNGC Error Status Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngcerrstatusreg */
+/*! @{ */
+/*! Unused or MBZ. */
+#define RNGC_ERROR_STATUS_ZEROS_MASK 0xffffff80
+/*! Bad Key Error Status */
+#define RNGC_ERROR_STATUS_BAD_KEY 0x00000040
+/*! Random Compare Error. Previous number matched the current number. */
+#define RNGC_ERROR_STATUS_RAND_ERR 0x00000020
+/*! FIFO Underflow. FIFO was read while empty. */
+#define RNGC_ERROR_STATUS_FIFO_ERR 0x00000010
+/*! Statistic Error Statistic Test failed for the last seed. */
+#define RNGC_ERROR_STATUS_STAT_ERR 0x00000008
+/*! Self-test error. Some self test has failed. */
+#define RNGC_ERROR_STATUS_ST_ERR 0x00000004
+/*!
+ * Oscillator Error. The oscillator may be broken. Clear by hard or soft
+ * reset.
+ */
+#define RNGC_ERROR_STATUS_OSC_ERR 0x00000002
+/*! LFSR Error. Clear by hard or soft reset. */
+#define RNGC_ERROR_STATUS_LFSR_ERR 0x00000001
+
+/*! @} */
+
+/*! Total address space of the RNGB/RNGC registers, in bytes */
+#define RNG_ADDRESS_RANGE 0x34
+
+#endif /* RNG_RNGC_H */
diff --git a/drivers/mxc/security/rng/include/shw_driver.h b/drivers/mxc/security/rng/include/shw_driver.h
new file mode 100644
index 000000000000..d529b4fe6e6b
--- /dev/null
+++ b/drivers/mxc/security/rng/include/shw_driver.h
@@ -0,0 +1,2971 @@
+/*
+ * Copyright (C) 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifndef SHW_DRIVER_H
+#define SHW_DRIVER_H
+
+/* This is a Linux flag meaning 'compiling kernel code'... */
+#ifndef __KERNEL__
+#include <inttypes.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <memory.h>
+#include <stdio.h>
+#else
+#include "../../sahara2/include/portable_os.h"
+#endif /* __KERNEL__ */
+
+#include "../../sahara2/include/fsl_platform.h"
+
+/*! @file shw_driver.h
+ *
+ * @brief Header file to use the SHW driver.
+ *
+ * The SHW driver is used in two modes: By a user, from the FSL SHW API in user
+ * space, which goes through /dev/fsl_shw to make open(), ioctl(), and close()
+ * calls; and by other kernel modules/drivers, which use the FSL SHW API, parts
+ * of which are supported directly by the SHW driver.
+ *
+ * Testing is performed by using the apitest and kernel api test routines
+ * developed for the Sahara2 driver.
+ */
+/*#define DIAG_SECURITY_FUNC*/
+/*! Perform a security function. */
+#define SHW_IOCTL_REQUEST 21
+
+/* This definition may need a new name, and needs to go somewhere which
+ * can determine platform, kernel vs. user, os, etc.
+ */
+#define copy_bytes(out, in, len) memcpy(out, in, len)
+
+
+/*!
+ * This is part of the IOCTL request type passed between kernel and user space.
+ * It is added to #SHW_IOCTL_REQUEST to generate the actual value.
+ */
+typedef enum shw_user_request_t {
+ SHW_USER_REQ_REGISTER_USER, /*!< Initialize user-kernel discussion. */
+ SHW_USER_REQ_DEREGISTER_USER, /*!< Terminate user-kernel discussion. */
+ SHW_USER_REQ_GET_RESULTS, /*!< Get information on outstanding
+ results. */
+ SHW_USER_REQ_GET_CAPABILITIES, /*!< Get information on hardware support. */
+ SHW_USER_REQ_GET_RANDOM, /*!< Get random data from RNG. */
+ SHW_USER_REQ_ADD_ENTROPY, /*!< Add entropy to hardware RNG. */
+ SHW_USER_REQ_DROP_PERMS, /*!< Diminish the permissions of a block of
+ secure memory */
+ SHW_USER_REQ_SSTATUS, /*!< Check the status of a block of secure
+ memory */
+ SHW_USER_REQ_SFREE, /*!< Free a block of secure memory */
+ SHW_USER_REQ_SCC_ENCRYPT, /*!< Encrypt a region of user-owned secure
+ memory */
+ SHW_USER_REQ_SCC_DECRYPT, /*!< Decrypt a region of user-owned secure
+ memory */
+} shw_user_request_t;
+
+
+/*!
+ * @typedef scc_partition_status_t
+ */
+/** Partition status information. */
+typedef enum fsl_shw_partition_status_t {
+ FSL_PART_S_UNUSABLE, /*!< Partition not implemented */
+ FSL_PART_S_UNAVAILABLE, /*!< Partition owned by other host */
+ FSL_PART_S_AVAILABLE, /*!< Partition available */
+ FSL_PART_S_ALLOCATED, /*!< Partition owned by host but not engaged
+ */
+ FSL_PART_S_ENGAGED, /*!< Partition owned by host and engaged */
+} fsl_shw_partition_status_t;
+
+
+/*
+ * Structure passed during user ioctl() calls to manage secure partitions.
+ */
+typedef struct scc_partition_info_t {
+ uint32_t user_base; /*!< Userspace pointer to base of partition */
+ uint32_t permissions; /*!< Permissions to give the partition (only
+ used in call to _DROP_PERMS) */
+ fsl_shw_partition_status_t status; /*!< Status of the partition */
+} scc_partition_info_t;
+
+
+/******************************************************************************
+ * Enumerations
+ *****************************************************************************/
+/*!
+ * Flags for the state of the User Context Object (#fsl_shw_uco_t).
+ */
+typedef enum fsl_shw_user_ctx_flags_t
+{
+ /*!
+ * API will block the caller until operation completes. The result will be
+ * available in the return code. If this is not set, user will have to get
+ * results using #fsl_shw_get_results().
+ */
+ FSL_UCO_BLOCKING_MODE = 0x01,
+ /*!
+ * User wants callback (at the function specified with
+ * #fsl_shw_uco_set_callback()) when the operation completes. This flag is
+ * valid only if #FSL_UCO_BLOCKING_MODE is not set.
+ */
+ FSL_UCO_CALLBACK_MODE = 0x02,
+ /*! Do not free descriptor chain after driver (adaptor) finishes */
+ FSL_UCO_SAVE_DESC_CHAIN = 0x04,
+ /*!
+ * User has made at least one request with callbacks requested, so API is
+ * ready to handle others.
+ */
+ FSL_UCO_CALLBACK_SETUP_COMPLETE = 0x08,
+ /*!
+ * (virtual) pointer to descriptor chain is completely linked with physical
+ * (DMA) addresses, ready for the hardware. This flag should not be used
+ * by FSL SHW API programs.
+ */
+ FSL_UCO_CHAIN_PREPHYSICALIZED = 0x10,
+ /*!
+ * The user has changed the context but the changes have not been copied to
+ * the kernel driver.
+ */
+ FSL_UCO_CONTEXT_CHANGED = 0x20,
+ /*! Internal Use. This context belongs to a user-mode API user. */
+ FSL_UCO_USERMODE_USER = 0x40,
+} fsl_shw_user_ctx_flags_t;
+
+
+/*!
+ * Return code for FSL_SHW library.
+ *
+ * These codes may be returned from a function call. In non-blocking mode,
+ * they will appear as the status in a Result Object.
+ */
+/* REQ-FSLSHW-ERR-001 */
+typedef enum fsl_shw_return_t
+{
+ /*!
+ * No error. As a function return code in Non-blocking mode, this may
+ * simply mean that the operation was accepted for eventual execution.
+ */
+ FSL_RETURN_OK_S = 0,
+ /*! Failure for non-specific reason. */
+ FSL_RETURN_ERROR_S,
+ /*!
+ * Operation failed because some resource was not able to be allocated.
+ */
+ FSL_RETURN_NO_RESOURCE_S,
+ /*! Crypto algorithm unrecognized or improper. */
+ FSL_RETURN_BAD_ALGORITHM_S,
+ /*! Crypto mode unrecognized or improper. */
+ FSL_RETURN_BAD_MODE_S,
+ /*! Flag setting unrecognized or inconsistent. */
+ FSL_RETURN_BAD_FLAG_S,
+ /*! Improper or unsupported key length for algorithm. */
+ FSL_RETURN_BAD_KEY_LENGTH_S,
+ /*! Improper parity in a (DES, TDES) key. */
+ FSL_RETURN_BAD_KEY_PARITY_S,
+ /*!
+ * Improper or unsupported data length for algorithm or internal buffer.
+ */
+ FSL_RETURN_BAD_DATA_LENGTH_S,
+ /*! Authentication / Integrity Check code check failed. */
+ FSL_RETURN_AUTH_FAILED_S,
+ /*! A memory error occurred. */
+ FSL_RETURN_MEMORY_ERROR_S,
+ /*! An error internal to the hardware occurred. */
+ FSL_RETURN_INTERNAL_ERROR_S,
+ /*! ECC detected Point at Infinity */
+ FSL_RETURN_POINT_AT_INFINITY_S,
+ /*! ECC detected No Point at Infinity */
+ FSL_RETURN_POINT_NOT_AT_INFINITY_S,
+ /*! GCD is One */
+ FSL_RETURN_GCD_IS_ONE_S,
+ /*! GCD is not One */
+ FSL_RETURN_GCD_IS_NOT_ONE_S,
+ /*! Candidate is Prime */
+ FSL_RETURN_PRIME_S,
+ /*! Candidate is not Prime */
+ FSL_RETURN_NOT_PRIME_S,
+ /*! N register loaded improperly with even value */
+ FSL_RETURN_EVEN_MODULUS_ERROR_S,
+ /*! Divisor is zero. */
+ FSL_RETURN_DIVIDE_BY_ZERO_ERROR_S,
+ /*! Bad Exponent or Scalar value for Point Multiply */
+ FSL_RETURN_BAD_EXPONENT_ERROR_S,
+ /*! RNG hardware problem. */
+ FSL_RETURN_OSCILLATOR_ERROR_S,
+ /*! RNG hardware problem. */
+ FSL_RETURN_STATISTICS_ERROR_S,
+} fsl_shw_return_t;
+
+
+/*!
+ * Algorithm Identifier.
+ *
+ * Selection of algorithm will determine how large the block size of the
+ * algorithm is. Context size is the same length unless otherwise specified.
+ * Selection of algorithm also affects the allowable key length.
+ */
+typedef enum fsl_shw_key_alg_t
+{
+ /*!
+ * Key will be used to perform an HMAC. Key size is 1 to 64 octets. Block
+ * size is 64 octets.
+ */
+ FSL_KEY_ALG_HMAC,
+ /*!
+ * Advanced Encryption Standard (Rijndael). Block size is 16 octets. Key
+ * size is 16 octets. (The single choice of key size is a Sahara platform
+ * limitation.)
+ */
+ FSL_KEY_ALG_AES,
+ /*!
+ * Data Encryption Standard. Block size is 8 octets. Key size is 8
+ * octets.
+ */
+ FSL_KEY_ALG_DES,
+ /*!
+ * 2- or 3-key Triple DES. Block size is 8 octets. Key size is 16 octets
+ * for 2-key Triple DES, and 24 octets for 3-key.
+ */
+ FSL_KEY_ALG_TDES,
+ /*!
+ * ARC4. No block size. Context size is 259 octets. Allowed key size is
+ * 1-16 octets. (The choices for key size are a Sahara platform
+ * limitation.)
+ */
+ FSL_KEY_ALG_ARC4,
+} fsl_shw_key_alg_t;
+
+
+/*!
+ * Mode selector for Symmetric Ciphers.
+ *
+ * The selection of mode determines how a cryptographic algorithm will be
+ * used to process the plaintext or ciphertext.
+ *
+ * For all modes which are run block-by-block (that is, all but
+ * #FSL_SYM_MODE_STREAM), any partial operations must be performed on a text
+ * length which is multiple of the block size. Except for #FSL_SYM_MODE_CTR,
+ * these block-by-block algorithms must also be passed a total number of octets
+ * which is a multiple of the block size.
+ *
+ * In modes which require that the total number of octets of data be a multiple
+ * of the block size (#FSL_SYM_MODE_ECB and #FSL_SYM_MODE_CBC), and the user
+ * has a total number of octets which are not a multiple of the block size, the
+ * user must perform any necessary padding to get to the correct data length.
+ */
+typedef enum fsl_shw_sym_mode_t
+{
+ /*!
+ * Stream. There is no associated block size. Any request to process data
+ * may be of any length. This mode is only for ARC4 operations, and is
+ * also the only mode used for ARC4.
+ */
+ FSL_SYM_MODE_STREAM,
+
+ /*!
+ * Electronic Codebook. Each block of data is encrypted/decrypted. The
+ * length of the data stream must be a multiple of the block size. This
+ * mode may be used for DES, 3DES, and AES. The block size is determined
+ * by the algorithm.
+ */
+ FSL_SYM_MODE_ECB,
+ /*!
+ * Cipher-Block Chaining. Each block of data is encrypted/decrypted and
+ * then "chained" with the previous block by an XOR function. Requires
+ * context to start the XOR (previous block). This mode may be used for
+ * DES, 3DES, and AES. The block size is determined by the algorithm.
+ */
+ FSL_SYM_MODE_CBC,
+ /*!
+ * Counter. The counter is encrypted, then XORed with a block of data.
+ * The counter is then incremented (using modulus arithmetic) for the next
+ * block. The final operation may be non-multiple of block size. This mode
+ * may be used for AES. The block size is determined by the algorithm.
+ */
+ FSL_SYM_MODE_CTR,
+} fsl_shw_sym_mode_t;
+
+
+/*!
+ * Algorithm selector for Cryptographic Hash functions.
+ *
+ * Selection of algorithm determines how large the context and digest will be.
+ * Context is the same size as the digest (resulting hash), unless otherwise
+ * specified.
+ */
+typedef enum fsl_shw_hash_alg_t
+{
+ FSL_HASH_ALG_MD5, /*!< MD5 algorithm. Digest is 16 octets. */
+ FSL_HASH_ALG_SHA1, /*!< SHA-1 (aka SHA or SHA-160) algorithm.
+ Digest is 20 octets. */
+ FSL_HASH_ALG_SHA224, /*!< SHA-224 algorithm. Digest is 28 octets,
+ though context is 32 octets. */
+ FSL_HASH_ALG_SHA256 /*!< SHA-256 algorithm. Digest is 32
+ octets. */
+} fsl_shw_hash_alg_t;
+
+
+/*!
+ * The type of Authentication-Cipher function which will be performed.
+ */
+typedef enum fsl_shw_acc_mode_t
+{
+ /*!
+ * CBC-MAC for Counter. Requires context and modulus. Final operation may
+ * be non-multiple of block size. This mode may be used for AES.
+ */
+ FSL_ACC_MODE_CCM,
+ /*!
+ * SSL mode. Not supported. Combines HMAC and encrypt (or decrypt).
+ * Needs one key object for encryption, another for the HMAC. The usual
+ * hashing and symmetric encryption algorithms are supported.
+ */
+ FSL_ACC_MODE_SSL
+} fsl_shw_acc_mode_t;
+
+
+/* REQ-FSLSHW-PINTFC-COA-HCO-001 */
+/*!
+ * Flags which control a Hash operation.
+ */
+typedef enum fsl_shw_hash_ctx_flags_t
+{
+ FSL_HASH_FLAGS_INIT = 0x01, /*!< Context is empty. Hash is started
+ from scratch, with a message-processed
+ count of zero. */
+ FSL_HASH_FLAGS_SAVE = 0x02, /*!< Retrieve context from hardware after
+ hashing. If used with the
+ #FSL_HASH_FLAGS_FINALIZE flag, the final
+ digest value will be saved in the
+ object. */
+ FSL_HASH_FLAGS_LOAD = 0x04, /*!< Place context into hardware before
+ hashing. */
+ FSL_HASH_FLAGS_FINALIZE = 0x08, /*!< PAD message and perform final digest
+ operation. If user message is
+ pre-padded, this flag should not be
+ used. */
+} fsl_shw_hash_ctx_flags_t;
+
+
+/*!
+ * Flags which control an HMAC operation.
+ *
+ * These may be combined by ORing them together. See #fsl_shw_hmco_set_flags()
+ * and #fsl_shw_hmco_clear_flags().
+ */
+typedef enum fsl_shw_hmac_ctx_flags_t
+{
+ FSL_HMAC_FLAGS_INIT = 1, /**< Message context is empty. HMAC is
+ started from scratch (with key) or from
+ precompute of inner hash, depending on
+ whether
+ #FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT is
+ set. */
+ FSL_HMAC_FLAGS_SAVE = 2, /**< Retrieve ongoing context from hardware
+ after hashing. If used with the
+ #FSL_HMAC_FLAGS_FINALIZE flag, the final
+ digest value (HMAC) will be saved in the
+ object. */
+ FSL_HMAC_FLAGS_LOAD = 4, /**< Place ongoing context into hardware
+ before hashing. */
+ FSL_HMAC_FLAGS_FINALIZE = 8, /**< PAD message and perform final HMAC
+ operations of inner and outer hashes. */
+ FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT = 16 /**< This means that the context
+ contains precomputed inner and outer
+ hash values. */
+} fsl_shw_hmac_ctx_flags_t;
+
+
+/**
+ * Flags to control use of the #fsl_shw_scco_t.
+ *
+ * These may be ORed together to get the desired effect.
+ * See #fsl_shw_scco_set_flags() and #fsl_shw_scco_clear_flags()
+ */
+typedef enum fsl_shw_sym_ctx_flags_t
+{
+ /**
+ * Context is empty. In ARC4, this means that the S-Box needs to be
+ * generated from the key. In #FSL_SYM_MODE_CBC mode, this allows an IV of
+ * zero to be specified. In #FSL_SYM_MODE_CTR mode, it means that an
+ * initial CTR value of zero is desired.
+ */
+ FSL_SYM_CTX_INIT = 1,
+ /**
+ * Load context from object into hardware before running cipher. In
+ * #FSL_SYM_MODE_CTR mode, this would refer to the Counter Value.
+ */
+ FSL_SYM_CTX_LOAD = 2,
+ /**
+ * Save context from hardware into object after running cipher. In
+ * #FSL_SYM_MODE_CTR mode, this would refer to the Counter Value.
+ */
+ FSL_SYM_CTX_SAVE = 4,
+ /**
+ * Context (SBox) is to be unwrapped and wrapped on each use.
+ * This flag is unsupported.
+ * */
+ FSL_SYM_CTX_PROTECT = 8,
+} fsl_shw_sym_ctx_flags_t;
+
+
+/**
+ * Flags which describe the state of the #fsl_shw_sko_t.
+ *
+ * These may be ORed together to get the desired effect.
+ * See #fsl_shw_sko_set_flags() and #fsl_shw_sko_clear_flags()
+ */
+typedef enum fsl_shw_key_flags_t
+{
+ FSL_SKO_KEY_IGNORE_PARITY = 1, /*!< If algorithm is DES or 3DES, do not
+ validate the key parity bits. */
+ FSL_SKO_KEY_PRESENT = 2, /*!< Clear key is present in the object. */
+ FSL_SKO_KEY_ESTABLISHED = 4, /*!< Key has been established for use. This
+ feature is not available for all
+ platforms, nor for all algorithms and
+ modes.*/
+ FSL_SKO_USE_SECRET_KEY = 8, /*!< Use device-unique key. Not always
+ available. */
+ FSL_SKO_KEY_SW_KEY = 16, /*!< Clear key can be provided to the user */
+ FSL_SKO_KEY_SELECT_PF_KEY = 32, /*!< Internal flag to show that this key
+ references one of the hardware keys, and
+ its value is in pf_key. */
+} fsl_shw_key_flags_t;
+
+
+/**
+ * Type of value which is associated with an established key.
+ */
+typedef uint64_t key_userid_t;
+
+
+/**
+ * Flags which describe the state of the #fsl_shw_acco_t.
+ *
+ * The @a FSL_ACCO_CTX_INIT and @a FSL_ACCO_CTX_FINALIZE flags, when used
+ * together, provide for a one-shot operation.
+ */
+typedef enum fsl_shw_auth_ctx_flags_t
+{
+ FSL_ACCO_CTX_INIT = 1, /**< Initialize Context(s) */
+ FSL_ACCO_CTX_LOAD = 2, /**< Load intermediate context(s).
+ This flag is unsupported. */
+ FSL_ACCO_CTX_SAVE = 4, /**< Save intermediate context(s).
+ This flag is unsupported. */
+ FSL_ACCO_CTX_FINALIZE = 8, /**< Create MAC during this operation. */
+ FSL_ACCO_NIST_CCM = 0x10, /**< Formatting of CCM input data is
+ performed by calls to
+ #fsl_shw_ccm_nist_format_ctr_and_iv() and
+ #fsl_shw_ccm_nist_update_ctr_and_iv(). */
+} fsl_shw_auth_ctx_flags_t;
+
+
+/**
+ * The operation which controls the behavior of #fsl_shw_establish_key().
+ *
+ * These values are passed to #fsl_shw_establish_key().
+ */
+typedef enum fsl_shw_key_wrap_t
+{
+ FSL_KEY_WRAP_CREATE, /**< Generate a key from random values. */
+ FSL_KEY_WRAP_ACCEPT, /**< Use the provided clear key. */
+ FSL_KEY_WRAP_UNWRAP /**< Unwrap a previously wrapped key. */
+} fsl_shw_key_wrap_t;
+
+
+/**
+ * Modulus Selector for CTR modes.
+ *
+ * The incrementing of the Counter value may be modified by a modulus. If no
+ * modulus is needed or desired for AES, use #FSL_CTR_MOD_128.
+ */
+typedef enum fsl_shw_ctr_mod_t
+{
+ FSL_CTR_MOD_8, /**< Run counter with modulus of 2^8. */
+ FSL_CTR_MOD_16, /**< Run counter with modulus of 2^16. */
+ FSL_CTR_MOD_24, /**< Run counter with modulus of 2^24. */
+ FSL_CTR_MOD_32, /**< Run counter with modulus of 2^32. */
+ FSL_CTR_MOD_40, /**< Run counter with modulus of 2^40. */
+ FSL_CTR_MOD_48, /**< Run counter with modulus of 2^48. */
+ FSL_CTR_MOD_56, /**< Run counter with modulus of 2^56. */
+ FSL_CTR_MOD_64, /**< Run counter with modulus of 2^64. */
+ FSL_CTR_MOD_72, /**< Run counter with modulus of 2^72. */
+ FSL_CTR_MOD_80, /**< Run counter with modulus of 2^80. */
+ FSL_CTR_MOD_88, /**< Run counter with modulus of 2^88. */
+ FSL_CTR_MOD_96, /**< Run counter with modulus of 2^96. */
+ FSL_CTR_MOD_104, /**< Run counter with modulus of 2^104. */
+ FSL_CTR_MOD_112, /**< Run counter with modulus of 2^112. */
+ FSL_CTR_MOD_120, /**< Run counter with modulus of 2^120. */
+ FSL_CTR_MOD_128 /**< Run counter with modulus of 2^128. */
+} fsl_shw_ctr_mod_t;
+
+
+/**
+ * A work type associated with a work/result queue request.
+ */
+typedef enum shw_work_type_t
+{
+ SHW_WORK_GET_RANDOM = 1, /**< fsl_shw_get_random() request. */
+ SHW_WORK_ADD_RANDOM, /**< fsl_shw_add_entropy() request. */
+} shw_work_type_t;
+
+
+/**
+ * Permissions flags for Secure Partitions
+ */
+typedef enum fsl_shw_permission_t
+{
+/** SCM Access Permission: Do not zeroize/deallocate partition on SMN Fail state */
+ FSL_PERM_NO_ZEROIZE = 0x80000000,
+/** SCM Access Permission: Enforce trusted key read in */
+ FSL_PERM_TRUSTED_KEY_READ = 0x40000000,
+/** SCM Access Permission: Ignore Supervisor/User mode in permission determination */
+ FSL_PERM_HD_S = 0x00000800,
+/** SCM Access Permission: Allow Read Access to Host Domain */
+ FSL_PERM_HD_R = 0x00000400,
+/** SCM Access Permission: Allow Write Access to Host Domain */
+ FSL_PERM_HD_W = 0x00000200,
+/** SCM Access Permission: Allow Execute Access to Host Domain */
+ FSL_PERM_HD_X = 0x00000100,
+/** SCM Access Permission: Allow Read Access to Trusted Host Domain */
+ FSL_PERM_TH_R = 0x00000040,
+/** SCM Access Permission: Allow Write Access to Trusted Host Domain */
+ FSL_PERM_TH_W = 0x00000020,
+/** SCM Access Permission: Allow Read Access to Other/World Domain */
+ FSL_PERM_OT_R = 0x00000004,
+/** SCM Access Permission: Allow Write Access to Other/World Domain */
+ FSL_PERM_OT_W = 0x00000002,
+/** SCM Access Permission: Allow Execute Access to Other/World Domain */
+ FSL_PERM_OT_X = 0x00000001,
+} fsl_shw_permission_t;
+
+/*!
+ * Select the cypher mode to use for partition cover/uncover operations.
+ *
+ * They currently map directly to the values used in the SCC2 driver, but this
+ * is not guarinteed behavior.
+ */
+typedef enum fsl_shw_cypher_mode_t
+{
+ FSL_SHW_CYPHER_MODE_ECB = 1, /*!< ECB mode */
+ FSL_SHW_CYPHER_MODE_CBC = 2, /*!< CBC mode */
+} fsl_shw_cypher_mode_t;
+
+/*!
+ * Which platform key should be presented for cryptographic use.
+ */
+typedef enum fsl_shw_pf_key_t {
+ FSL_SHW_PF_KEY_IIM, /*!< Present fused IIM key */
+ FSL_SHW_PF_KEY_PRG, /*!< Present Program key */
+ FSL_SHW_PF_KEY_IIM_PRG, /*!< Present IIM ^ Program key */
+ FSL_SHW_PF_KEY_IIM_RND, /*!< Present Random key */
+ FSL_SHW_PF_KEY_RND, /*!< Present IIM ^ Random key */
+} fsl_shw_pf_key_t;
+
+/*!
+ * The various security tamper events
+ */
+typedef enum fsl_shw_tamper_t {
+ FSL_SHW_TAMPER_NONE, /*!< No error detected */
+ FSL_SHW_TAMPER_WTD, /*!< wire-mesh tampering det */
+ FSL_SHW_TAMPER_ETBD, /*!< ext tampering det: input B */
+ FSL_SHW_TAMPER_ETAD, /*!< ext tampering det: input A */
+ FSL_SHW_TAMPER_EBD, /*!< external boot detected */
+ FSL_SHW_TAMPER_SAD, /*!< security alarm detected */
+ FSL_SHW_TAMPER_TTD, /*!< temperature tampering det */
+ FSL_SHW_TAMPER_CTD, /*!< clock tampering det */
+ FSL_SHW_TAMPER_VTD, /*!< voltage tampering det */
+ FSL_SHW_TAMPER_MCO, /*!< monotonic counter overflow */
+ FSL_SHW_TAMPER_TCO, /*!< time counter overflow */
+} fsl_shw_tamper_t;
+
+/*
+ * Structure passed during user ioctl() calls to manage data stored in secure
+ * partitions.
+ */
+
+typedef struct scc_region_t {
+ uint32_t partition_base; /*!< Base address of partition */
+ uint32_t offset; /*!< Byte offset into partition */
+ uint32_t length; /*!< Number of bytes in request */
+ uint8_t *black_data; /*!< Address of cipher text */
+ uint64_t owner_id; /*!< user's secret */
+ fsl_shw_cypher_mode_t cypher_mode; /*!< ECB or CBC */
+ uint32_t IV[4]; /*!< IV for CBC mode */
+} scc_region_t;
+
+/******************************************************************************
+ * Data Structures
+ *****************************************************************************/
+
+/**
+ * Initialization Object
+ */
+typedef struct fsl_sho_ibo
+{
+} fsl_sho_ibo_t;
+
+
+/**
+ * Common Entry structure for work queues, results queues.
+ */
+typedef struct shw_queue_entry_t {
+ struct shw_queue_entry_t* next; /**< Next entry in queue. */
+ struct fsl_shw_uco_t* user_ctx; /**< Associated user context. */
+ uint32_t flags; /**< User context flags at time of request. */
+ void (*callback)(struct fsl_shw_uco_t* uco); /**< Any callback request. */
+ uint32_t user_ref; /**< User's reference for this request. */
+ fsl_shw_return_t code; /**< FSL SHW result of this operation. */
+ uint32_t detail1; /**< Any extra error info. */
+ uint32_t detail2; /**< More any extra error info. */
+ void* user_mode_req; /**< Pointer into user space. */
+ uint32_t (*postprocess)(struct shw_queue_entry_t* q); /**< (internal)
+ function to call
+ when this operation
+ completes.
+ */
+} shw_queue_entry_t;
+
+
+/**
+ * A queue. Fields must be initialized to NULL before use.
+ */
+typedef struct shw_queue_t
+{
+ struct shw_queue_entry_t* head; /**< First entry in queue. */
+ struct shw_queue_entry_t* tail; /**< Last entry. */
+} shw_queue_t;
+
+
+/**
+ * Secure Partition information
+ */
+typedef struct fsl_shw_spo_t
+{
+ uint32_t user_base;
+ void* kernel_base;
+ struct fsl_shw_spo_t* next;
+} fsl_shw_spo_t;
+
+
+/* REQ-FSLSHW-PINTFC-COA-UCO-001 */
+/**
+ * User Context Object
+ */
+typedef struct fsl_shw_uco_t
+{
+ int openfd; /**< user-mode file descriptor */
+ uint32_t user_ref; /**< User's reference */
+ void (*callback)(struct fsl_shw_uco_t* uco); /**< User's callback fn */
+ uint32_t flags; /**< from fsl_shw_user_ctx_flags_t */
+ unsigned pool_size; /**< maximum size of user result pool */
+#ifdef __KERNEL__
+ shw_queue_t result_pool; /**< where non-blocking results go */
+ os_process_handle_t process; /**< remember for signalling User mode */
+ fsl_shw_spo_t* partition; /**< chain of secure partitions owned by
+ the user */
+#endif
+ struct fsl_shw_uco_t* next; /**< To allow user-mode chaining of contexts,
+ for signalling and in kernel, to link user
+ contexts. */
+ fsl_shw_pf_key_t wrap_key; /*!< What key for ciphering T */
+} fsl_shw_uco_t;
+
+
+/* REQ-FSLSHW-PINTFC-API-GEN-006 ?? */
+/**
+ * Result object
+ */
+typedef struct fsl_shw_result_t
+{
+ uint32_t user_ref; /**< User's reference at time of request. */
+ fsl_shw_return_t code; /**< Return code from request. */
+ uint32_t detail1; /**< Extra error info. Unused in SHW driver. */
+ uint32_t detail2; /**< Extra error info. Unused in SHW driver. */
+ void* user_req; /**< Pointer to original user request. */
+} fsl_shw_result_t;
+
+
+/**
+ * Keystore Object
+ */
+typedef struct fsl_shw_kso_t
+{
+#ifdef __KERNEL__
+ os_lock_t lock; /**< Pointer to lock that controls access to
+ the keystore. */
+#endif
+ void* user_data; /**< Pointer to user structure that handles
+ the internals of the keystore. */
+ fsl_shw_return_t (*data_init) (fsl_shw_uco_t* user_ctx,
+ void** user_data);
+ void (*data_cleanup) (fsl_shw_uco_t* user_ctx,
+ void** user_data);
+ fsl_shw_return_t (*slot_verify_access)(void* user_data, uint64_t owner_id,
+ uint32_t slot);
+ fsl_shw_return_t (*slot_alloc) (void* user_data, uint32_t size_bytes,
+ uint64_t owner_id, uint32_t* slot);
+ fsl_shw_return_t (*slot_dealloc) (void* user_data,
+ uint64_t owner_id, uint32_t slot);
+ void* (*slot_get_address) (void* user_data, uint32_t slot);
+ uint32_t (*slot_get_base) (void* user_data, uint32_t slot);
+ uint32_t (*slot_get_offset) (void* user_data, uint32_t slot);
+ uint32_t (*slot_get_slot_size) (void* user_data, uint32_t slot);
+} fsl_shw_kso_t;
+
+
+/* REQ-FSLSHW-PINTFC-COA-SKO-001 */
+/**
+ * Secret Key Context Object
+ */
+typedef struct fsl_shw_sko_t
+{
+ uint32_t flags; /**< Flags from #fsl_shw_sym_ctx_flags_t. */
+ fsl_shw_key_alg_t algorithm; /**< Algorithm for this key. */
+ key_userid_t userid; /**< User's identifying value for Black key. */
+ uint32_t handle; /**< Reference in SCC driver for Red key. */
+ uint16_t key_length; /**< Length of stored key, in bytes. */
+ uint8_t key[64]; /**< Bytes of stored key. */
+ struct fsl_shw_kso_t* keystore; /**< If present, key is in keystore */
+ fsl_shw_pf_key_t pf_key; /*!< What key to select for use when this key
+ is doing ciphering. If FSL_SHW_PF_KEY_PRG
+ or FSL_SHW_PF_KEY_PRG_IIM is the value, then
+ a 'present' or 'established' key will be
+ programed into the PK. */
+} fsl_shw_sko_t;
+
+
+/* REQ-FSLSHW-PINTFC-COA-CO-001 */
+/**
+ * Platform Capability Object
+ *
+ * Pointer to this structure is returned by fsl_shw_get_capabilities() and
+ * queried with the various fsl_shw_pco_() functions.
+ */
+typedef struct fsl_shw_pco_t
+{
+ int api_major; /**< Major version number for API. */
+ int api_minor; /**< Minor version number for API. */
+ int driver_major; /**< Major version of some driver. */
+ int driver_minor; /**< Minor version of some driver. */
+ unsigned sym_algorithm_count; /**< Number of sym_algorithms. */
+ fsl_shw_key_alg_t* sym_algorithms; /**< Pointer to array. */
+ unsigned sym_mode_count; /**< Number of sym_modes. */
+ fsl_shw_sym_mode_t* sym_modes; /**< Pointer to array. */
+ unsigned hash_algorithm_count; /**< Number of hash_algorithms. */
+ fsl_shw_hash_alg_t* hash_algorithms; /**< Pointer to array */
+ uint8_t sym_support[5][4]; /**< indexed by key alg then mode */
+
+ int scc_driver_major;
+ int scc_driver_minor;
+ int scm_version; /**< Version from SCM Configuration register */
+ int smn_version; /**< Version from SMN Status register */
+ int block_size_bytes; /**< Number of bytes per block of RAM; also
+ block size of the crypto algorithm. */
+ union {
+ struct scc_info {
+ int black_ram_size_blocks; /**< Number of blocks of Black RAM */
+ int red_ram_size_blocks; /**< Number of blocks of Red RAM */
+ } scc_info;
+ struct scc2_info {
+ int partition_size_bytes; /**< Number of bytes in each partition */
+ int partition_count; /**< Number of partitions on this platform */
+ } scc2_info;
+ } u;
+} fsl_shw_pco_t;
+
+
+/* REQ-FSLSHW-PINTFC-COA-HCO-001 */
+/**
+ * Hash Context Object
+ */
+typedef struct fsl_shw_hco_t /* fsl_shw_hash_context_object */
+{
+ fsl_shw_hash_alg_t algorithm; /**< Algorithm for this context. */
+ uint32_t flags; /**< Flags from
+ #fsl_shw_hash_ctx_flags_t. */
+ uint8_t digest_length; /**< hash result length in bytes */
+ uint8_t context_length; /**< Context length in bytes */
+ uint8_t context_register_length; /**< in bytes */
+ uint32_t context[9]; /**< largest digest + msg size */
+} fsl_shw_hco_t;
+
+
+/* REQ-FSLSHW-PINTFC-COA-HCO-001 */
+/**
+ * HMAC Context Object
+ */
+typedef struct fsl_shw_hmco_t /* fsl_shw_hmac_context_object */
+{
+ fsl_shw_hash_alg_t algorithm; /**< Hash algorithm for the HMAC. */
+ uint32_t flags; /**< Flags from
+ #fsl_shw_hmac_ctx_flags_t. */
+ uint8_t digest_length; /**< in bytes */
+ uint8_t context_length; /**< in bytes */
+ uint8_t context_register_length; /**< in bytes */
+ uint32_t ongoing_context[9]; /**< largest digest + msg
+ size */
+ uint32_t inner_precompute[9]; /**< largest digest + msg
+ size */
+ uint32_t outer_precompute[9]; /**< largest digest + msg
+ size */
+} fsl_shw_hmco_t;
+
+
+/* REQ-FSLSHW-PINTFC-COA-SCCO-001 */
+/**
+ * Symmetric Crypto Context Object Context Object
+ */
+typedef struct fsl_shw_scco_t
+{
+ uint32_t flags; /**< Flags from #fsl_shw_sym_ctx_flags_t. */
+ unsigned block_size_bytes; /**< Both block and ctx size */
+ fsl_shw_sym_mode_t mode; /**< Symmetric mode for this context. */
+ /* Could put modulus plus 16-octet context in union with arc4
+ sbox+ptrs... */
+ fsl_shw_ctr_mod_t modulus_exp; /**< Exponent value for CTR modulus */
+ uint8_t context[8]; /**< Stored context. Large enough
+ for 3DES. */
+} fsl_shw_scco_t;
+
+
+/**
+ * Authenticate-Cipher Context Object
+
+ * An object for controlling the function of, and holding information about,
+ * data for the authenticate-cipher functions, #fsl_shw_gen_encrypt() and
+ * #fsl_shw_auth_decrypt().
+ */
+typedef struct fsl_shw_acco_t
+{
+ uint32_t flags; /**< See #fsl_shw_auth_ctx_flags_t for
+ meanings */
+ fsl_shw_acc_mode_t mode; /**< CCM only */
+ uint8_t mac_length; /**< User's value for length */
+ unsigned q_length; /**< NIST parameter - */
+ fsl_shw_scco_t cipher_ctx_info; /**< For running
+ encrypt/decrypt. */
+ union {
+ fsl_shw_scco_t CCM_ctx_info; /**< For running the CBC in
+ AES-CCM. */
+ fsl_shw_hco_t hash_ctx_info; /**< For running the hash */
+ } auth_info; /**< "auth" info struct */
+ uint8_t unencrypted_mac[16]; /**< max block size... */
+} fsl_shw_acco_t;
+
+
+/**
+ * Common header in request structures between User-mode API and SHW driver.
+ */
+struct shw_req_header {
+ uint32_t flags; /**< Flags - from user-mode context. */
+ uint32_t user_ref; /**< Reference - from user-mode context. */
+ fsl_shw_return_t code; /**< Result code for operation. */
+};
+
+/**
+ * Used by user-mode API to retrieve completed non-blocking results in
+ * SHW_USER_REQ_GET_RESULTS ioctl().
+ */
+struct results_req {
+ struct shw_req_header hdr; /**< Boilerplate. */
+ unsigned requested; /**< number of results requested, */
+ unsigned actual; /**< number of results obtained. */
+ fsl_shw_result_t *results; /**< pointer to memory to hold results. */
+};
+
+
+/**
+ * Used by user-mode API to retrieve hardware capabilities in
+ * SHW_USER_REQ_GET_CAPABILITIES ioctl().
+ */
+struct capabilities_req {
+ struct shw_req_header hdr; /**< Boilerplate. */
+ unsigned size; /**< Size, in bytes, capabilities. */
+ fsl_shw_pco_t* capabilities; /**< Place to copy out the info. */
+};
+
+
+/**
+ * Used by user-mode API to get a random number
+ */
+struct get_random_req {
+ struct shw_req_header hdr; /**< Boilerplate. */
+ unsigned size; /**< Size, in bytes, of random. */
+ uint8_t* random; /**< Place to copy out the random number. */
+};
+
+
+/**
+ * Used by API to add entropy to a random number generator
+ */
+struct add_entropy_req {
+ struct shw_req_header hdr; /**< Boilerplate. */
+ unsigned size; /**< Size, in bytes, of entropy. */
+ uint8_t* entropy; /**< Location of the entropy to be added. */
+};
+
+
+/******************************************************************************
+ * External variables
+ *****************************************************************************/
+#ifdef __KERNEL__
+extern os_lock_t shw_queue_lock;
+
+extern fsl_shw_uco_t* user_list;
+#endif
+
+
+/******************************************************************************
+ * Access Macros for Objects
+ *****************************************************************************/
+/**
+ * Get FSL SHW API version
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] pcmajor A pointer to where the major version
+ * of the API is to be stored.
+ * @param[out] pcminor A pointer to where the minor version
+ * of the API is to be stored.
+ */
+#define fsl_shw_pco_get_version(pcobject, pcmajor, pcminor) \
+do { \
+ *(pcmajor) = (pcobject)->api_major; \
+ *(pcminor) = (pcobject)->api_minor; \
+} while (0)
+
+
+/**
+ * Get underlying driver version.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] pcmajor A pointer to where the major version
+ * of the driver is to be stored.
+ * @param[out] pcminor A pointer to where the minor version
+ * of the driver is to be stored.
+ */
+#define fsl_shw_pco_get_driver_version(pcobject, pcmajor, pcminor) \
+do { \
+ *(pcmajor) = (pcobject)->driver_major; \
+ *(pcminor) = (pcobject)->driver_minor; \
+} while (0)
+
+
+/**
+ * Get list of symmetric algorithms supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] pcalgorithms A pointer to where to store the location of
+ * the list of algorithms.
+ * @param[out] pcacount A pointer to where to store the number of
+ * algorithms in the list at @a algorithms.
+ */
+#define fsl_shw_pco_get_sym_algorithms(pcobject, pcalgorithms, pcacount) \
+do { \
+ *(pcalgorithms) = (pcobject)->sym_algorithms; \
+ *(pcacount) = (pcobject)->sym_algorithm_count; \
+} while (0)
+
+
+/**
+ * Get list of symmetric modes supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] gsmodes A pointer to where to store the location of
+ * the list of modes.
+ * @param[out] gsacount A pointer to where to store the number of
+ * algorithms in the list at @a modes.
+ */
+#define fsl_shw_pco_get_sym_modes(pcobject, gsmodes, gsacount) \
+do { \
+ *(gsmodes) = (pcobject)->sym_modes; \
+ *(gsacount) = (pcobject)->sym_mode_count; \
+} while (0)
+
+
+/**
+ * Get list of hash algorithms supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] gsalgorithms A pointer which will be set to the list of
+ * algorithms.
+ * @param[out] gsacount The number of algorithms in the list at @a
+ * algorithms.
+ */
+#define fsl_shw_pco_get_hash_algorithms(pcobject, gsalgorithms, gsacount) \
+do { \
+ *(gsalgorithms) = (pcobject)->hash_algorithms; \
+ *(gsacount) = (pcobject)->hash_algorithm_count; \
+} while (0)
+
+
+/**
+ * Determine whether the combination of a given symmetric algorithm and a given
+ * mode is supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param pcalg A Symmetric Cipher algorithm.
+ * @param pcmode A Symmetric Cipher mode.
+ *
+ * @return 0 if combination is not supported, non-zero if supported.
+ */
+#if defined(FSL_HAVE_DRYICE) && defined(__KERNEL__)
+#define fsl_shw_pco_check_sym_supported(pcobject, pcalg, pcmode) \
+ ((pcobject)->sym_support[pcalg][pcmode])
+#else
+#define fsl_shw_pco_check_sym_supported(pcobject, pcalg, pcmode) \
+ 0
+#endif
+
+/**
+ * Determine whether a given Encryption-Authentication mode is supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param pcmode The Authentication mode.
+ *
+ * @return 0 if mode is not supported, non-zero if supported.
+ */
+#define fsl_shw_pco_check_auth_supported(pcobject, pcmode) \
+ 0
+
+
+/**
+ * Determine whether Black Keys (key establishment / wrapping) is supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ *
+ * @return 0 if wrapping is not supported, non-zero if supported.
+ */
+#if defined(FSL_HAVE_DRYICE) && defined(__KERNEL__)
+#define fsl_shw_pco_check_black_key_supported(pcobject) \
+ 1
+#else
+#define fsl_shw_pco_check_black_key_supported(pcobject) \
+ 0
+
+#endif
+
+/*!
+ * Determine whether Programmed Key features are available
+ *
+ * @param pcobject The Platform Capabilities Object to query.
+ *
+ * @return 1 if Programmed Key features are available, otherwise zero.
+ */
+#if defined(FSL_HAVE_DRYICE) && defined(__KERNEL__)
+#define fsl_shw_pco_check_pk_supported(pcobject) \
+ 1
+#else
+#define fsl_shw_pco_check_pk_supported(pcobject) \
+ 0
+#endif
+
+/*!
+ * Determine whether Software Key features are available
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ *
+ * @return 1 if Software key features are available, otherwise zero.
+ */
+#if defined(FSL_HAVE_DRYICE) && defined(__KERNEL__)
+#define fsl_shw_pco_check_sw_keys_supported(pcobject) \
+ 1
+#else
+#define fsl_shw_pco_check_sw_keys_supported(pcobject) \
+ 0
+#endif
+
+/*!
+ * Get FSL SHW SCC driver version
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] pcmajor A pointer to where the major version
+ * of the SCC driver is to be stored.
+ * @param[out] pcminor A pointer to where the minor version
+ * of the SCC driver is to be stored.
+ */
+#define fsl_shw_pco_get_scc_driver_version(pcobject, pcmajor, pcminor) \
+{ \
+ *(pcmajor) = (pcobject)->scc_driver_major; \
+ *(pcminor) = (pcobject)->scc_driver_minor; \
+}
+
+
+/**
+ * Get SCM hardware version
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @return The SCM hardware version
+ */
+#define fsl_shw_pco_get_scm_version(pcobject) \
+ ((pcobject)->scm_version)
+
+
+/**
+ * Get SMN hardware version
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @return The SMN hardware version
+ */
+#define fsl_shw_pco_get_smn_version(pcobject) \
+ ((pcobject)->smn_version)
+
+
+/**
+ * Get the size of an SCM block, in bytes
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @return The size of an SCM block, in bytes.
+ */
+#define fsl_shw_pco_get_scm_block_size(pcobject) \
+ ((pcobject)->block_size_bytes)
+
+
+/**
+ * Get size of Black and Red RAM memory
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] black_size A pointer to where the size of the Black RAM, in
+ * blocks, is to be placed.
+ * @param[out] red_size A pointer to where the size of the Red RAM, in
+ * blocks, is to be placed.
+ */
+#define fsl_shw_pco_get_smn_size(pcobject, black_size, red_size) \
+{ \
+ if ((pcobject)->scm_version == 1) { \
+ *(black_size) = (pcobject)->u.scc_info.black_ram_size_blocks; \
+ *(red_size) = (pcobject)->u.scc_info.red_ram_size_blocks; \
+ } else { \
+ *(black_size) = 0; \
+ *(red_size) = 0; \
+ } \
+}
+
+
+/**
+ * Determine whether Secure Partitions are supported
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ *
+ * @return 0 if secure partitions are not supported, non-zero if supported.
+ */
+#define fsl_shw_pco_check_spo_supported(pcobject) \
+ ((pcobject)->scm_version == 2)
+
+
+/**
+ * Get the size of a Secure Partitions
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ *
+ * @return Partition size, in bytes. 0 if Secure Partitions not supported.
+ */
+#define fsl_shw_pco_get_spo_size_bytes(pcobject) \
+ (((pcobject)->scm_version == 2) ? \
+ ((pcobject)->u.scc2_info.partition_size_bytes) : 0 ) \
+
+
+/**
+ * Get the number of Secure Partitions on this platform
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ *
+ * @return Number of partitions. 0 if Secure Paritions not supported. Note
+ * that this returns the total number of partitions, not all may be
+ * available to the user.
+ */
+#define fsl_shw_pco_get_spo_count(pcobject) \
+ (((pcobject)->scm_version == 2) ? \
+ ((pcobject)->u.scc2_info.partition_count) : 0 ) \
+
+
+/*!
+ * Initialize a Secret Key Object.
+ *
+ * This function must be called before performing any other operation with
+ * the Object.
+ *
+ * @param skobject The Secret Key Object to be initialized.
+ * @param skalgorithm DES, AES, etc.
+ *
+ */
+#define fsl_shw_sko_init(skobject,skalgorithm) \
+{ \
+ fsl_shw_sko_t* skop = skobject; \
+ \
+ skop->algorithm = skalgorithm; \
+ skop->flags = 0; \
+ skop->keystore = NULL; \
+ skop->pf_key = FSL_SHW_PF_KEY_PRG; \
+}
+
+/*!
+ * Initialize a Secret Key Object to use a Platform Key register.
+ *
+ * This function must be called before performing any other operation with
+ * the Object.
+ *
+ * @param skobject The Secret Key Object to be initialized.
+ * @param skalgorithm DES, AES, etc.
+ * @param skhwkey one of the fsl_shw_pf_key_t values.
+ *
+ */
+#define fsl_shw_sko_init_pf_key(skobject,skalgorithm,skhwkey) \
+{ \
+ fsl_shw_sko_t* skop = skobject; \
+ fsl_shw_key_alg_t alg = skalgorithm; \
+ fsl_shw_pf_key_t key = skhwkey; \
+ \
+ skop->algorithm = alg; \
+ if (alg == FSL_KEY_ALG_TDES) { \
+ skop->key_length = 21; \
+ } \
+ skop->keystore = NULL; \
+ skop->flags = FSL_SKO_KEY_SELECT_PF_KEY; \
+ skop->pf_key = key; \
+ if ((key == FSL_SHW_PF_KEY_IIM) || (key == FSL_SHW_PF_KEY_PRG) \
+ || (key == FSL_SHW_PF_KEY_IIM_PRG) \
+ || (key == FSL_SHW_PF_KEY_IIM_RND) \
+ || (key == FSL_SHW_PF_KEY_RND)) { \
+ skop->flags |= FSL_SKO_KEY_ESTABLISHED; \
+ } \
+}
+
+/*!
+ * Store a cleartext key in the key object.
+ *
+ * This has the side effect of setting the #FSL_SKO_KEY_PRESENT flag and
+ * resetting the #FSL_SKO_KEY_ESTABLISHED flag.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skkey A pointer to the beginning of the key.
+ * @param skkeylen The length, in octets, of the key. The value should be
+ * appropriate to the key size supported by the algorithm.
+ * 64 octets is the absolute maximum value allowed for this
+ * call.
+ */
+#define fsl_shw_sko_set_key(skobject, skkey, skkeylen) \
+{ \
+ (skobject)->key_length = skkeylen; \
+ copy_bytes((skobject)->key, skkey, skkeylen); \
+ (skobject)->flags |= FSL_SKO_KEY_PRESENT; \
+ (skobject)->flags &= ~FSL_SKO_KEY_ESTABLISHED; \
+}
+
+/**
+ * Set a size for the key.
+ *
+ * This function would normally be used when the user wants the key to be
+ * generated from a random source.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skkeylen The length, in octets, of the key. The value should be
+ * appropriate to the key size supported by the algorithm.
+ * 64 octets is the absolute maximum value allowed for this
+ * call.
+ */
+#define fsl_shw_sko_set_key_length(skobject, skkeylen) \
+ (skobject)->key_length = skkeylen;
+
+
+/**
+ * Set the User ID associated with the key.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skuserid The User ID to identify authorized users of the key.
+ */
+#define fsl_shw_sko_set_user_id(skobject, skuserid) \
+ (skobject)->userid = (skuserid)
+
+/**
+ * Establish a user Keystore to hold the key.
+ */
+#define fsl_shw_sko_set_keystore(skobject, user_keystore) \
+ (skobject)->keystore = (user_keystore)
+
+
+
+/**
+ * Set the establish key handle into a key object.
+ *
+ * The @a userid field will be used to validate the access to the unwrapped
+ * key. This feature is not available for all platforms, nor for all
+ * algorithms and modes.
+ *
+ * The #FSL_SKO_KEY_ESTABLISHED will be set (and the #FSL_SKO_KEY_PRESENT flag
+ * will be cleared).
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skuserid The User ID to verify this user is an authorized user of
+ * the key.
+ * @param skhandle A @a handle from #fsl_shw_sko_get_established_info.
+ */
+#define fsl_shw_sko_set_established_info(skobject, skuserid, skhandle) \
+{ \
+ (skobject)->userid = (skuserid); \
+ (skobject)->handle = (skhandle); \
+ (skobject)->flags |= FSL_SKO_KEY_ESTABLISHED; \
+ (skobject)->flags &= \
+ ~(FSL_SKO_KEY_PRESENT); \
+}
+
+
+/**
+ * Retrieve the established-key handle from a key object.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skhandle The location to store the @a handle of the unwrapped
+ * key.
+ */
+#define fsl_shw_sko_get_established_info(skobject, skhandle) \
+ *(skhandle) = (skobject)->handle
+
+
+/**
+ * Extract the algorithm from a key object.
+ *
+ * @param skobject The Key Object to be queried.
+ * @param[out] skalgorithm A pointer to the location to store the algorithm.
+ */
+#define fsl_shw_sko_get_algorithm(skobject, skalgorithm) \
+ *(skalgorithm) = (skobject)->algorithm
+
+
+/**
+ * Retrieve the cleartext key from a key object that is stored in a user
+ * keystore.
+ *
+ * @param skobject The Key Object to be queried.
+ * @param[out] skkey A pointer to the location to store the key. NULL
+ * if the key is not stored in a user keystore.
+ */
+#define fsl_shw_sko_get_key(skobject, skkey) \
+{ \
+ fsl_shw_kso_t* keystore = (skobject)->keystore; \
+ if (keystore != NULL) { \
+ *(skkey) = keystore->slot_get_address(keystore->user_data, \
+ (skobject)->handle); \
+ } else { \
+ *(skkey) = NULL; \
+ } \
+}
+
+
+/*!
+ * Determine the size of a wrapped key based upon the cleartext key's length.
+ *
+ * This function can be used to calculate the number of octets that
+ * #fsl_shw_extract_key() will write into the location at @a covered_key.
+ *
+ * If zero is returned at @a length, this means that the key length in
+ * @a key_info is not supported.
+ *
+ * @param wkeyinfo Information about a key to be wrapped.
+ * @param wkeylen Location to store the length of a wrapped
+ * version of the key in @a key_info.
+ */
+#define fsl_shw_sko_calculate_wrapped_size(wkeyinfo, wkeylen) \
+{ \
+ register fsl_shw_sko_t* kp = wkeyinfo; \
+ register uint32_t kl = kp->key_length; \
+ int key_blocks; \
+ int base_size = 35; /* ICV + T' + ALG + LEN + FLAGS */ \
+ \
+ if (kp->flags & FSL_SKO_KEY_SELECT_PF_KEY) { \
+ kl = 21; /* 168-bit 3DES key */ \
+ } \
+ key_blocks = (kl + 7) / 8; \
+ /* Round length up to 3DES block size for CBC mode */ \
+ *(wkeylen) = base_size + 8 * key_blocks; \
+}
+
+/*!
+ * Set some flags in the key object.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skflags (One or more) ORed members of #fsl_shw_key_flags_t which
+ * are to be set.
+ */
+#define fsl_shw_sko_set_flags(skobject, skflags) \
+ (skobject)->flags |= (skflags)
+
+
+/**
+ * Clear some flags in the key object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skflags (One or more) ORed members of #fsl_shw_key_flags_t
+ * which are to be reset.
+ */
+#define fsl_shw_sko_clear_flags(skobject, skflags) \
+ (skobject)->flags &= ~(skflags)
+
+/**
+ * Initialize a User Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the User Context Object to initial values, and set the size
+ * of the results pool. The mode will be set to a default of
+ * #FSL_UCO_BLOCKING_MODE.
+ *
+ * When using non-blocking operations, this sets the maximum number of
+ * operations which can be outstanding. This number includes the counts of
+ * operations waiting to start, operation(s) being performed, and results which
+ * have not been retrieved.
+ *
+ * Changes to this value are ignored once user registration has completed. It
+ * should be set to 1 if only blocking operations will ever be performed.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param usize The maximum number of operations which can be
+ * outstanding.
+ */
+#ifdef __KERNEL__
+
+#define fsl_shw_uco_init(ucontext, usize) \
+do { \
+ fsl_shw_uco_t* uco = ucontext; \
+ \
+ (uco)->pool_size = usize; \
+ (uco)->flags = FSL_UCO_BLOCKING_MODE | FSL_UCO_CONTEXT_CHANGED; \
+ (uco)->openfd = -1; \
+ (uco)->callback = NULL; \
+ (uco)->partition = NULL; \
+ (uco)->wrap_key = FSL_SHW_PF_KEY_IIM; \
+} while (0)
+
+#else /* __KERNEL__ */
+
+#define fsl_shw_uco_init(ucontext, usize) \
+do { \
+ fsl_shw_uco_t* uco = ucontext; \
+ \
+ (uco)->pool_size = usize; \
+ (uco)->flags = FSL_UCO_BLOCKING_MODE | FSL_UCO_CONTEXT_CHANGED; \
+ (uco)->openfd = -1; \
+ (uco)->callback = NULL; \
+ (uco)->wrap_key = FSL_SHW_PF_KEY_IIM; \
+} while (0)
+
+#endif /* __KERNEL__ */
+
+
+/**
+ * Set the User Reference for the User Context.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param uref A value which will be passed back with a result.
+ */
+#define fsl_shw_uco_set_reference(ucontext, uref) \
+do { \
+ fsl_shw_uco_t* uco = ucontext; \
+ \
+ (uco)->user_ref = uref; \
+ (uco)->flags |= FSL_UCO_CONTEXT_CHANGED; \
+} while (0)
+
+
+/**
+ * Set the User Reference for the User Context.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param ucallback The function the API will invoke when an operation
+ * completes.
+ */
+#define fsl_shw_uco_set_callback(ucontext, ucallback) \
+do { \
+ fsl_shw_uco_t* uco = ucontext; \
+ \
+ (uco)->callback = ucallback; \
+ (uco)->flags |= FSL_UCO_CONTEXT_CHANGED; \
+} while (0)
+
+/**
+ * Set flags in the User Context.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param uflags ORed values from #fsl_shw_user_ctx_flags_t.
+ */
+#define fsl_shw_uco_set_flags(ucontext, uflags) \
+ (ucontext)->flags |= (uflags) | FSL_UCO_CONTEXT_CHANGED
+
+
+/**
+ * Clear flags in the User Context.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param uflags ORed values from #fsl_shw_user_ctx_flags_t.
+ */
+#define fsl_shw_uco_clear_flags(ucontext, uflags) \
+do { \
+ fsl_shw_uco_t* uco = ucontext; \
+ \
+ (uco)->flags &= ~(uflags); \
+ (uco)->flags |= FSL_UCO_CONTEXT_CHANGED; \
+} while (0)
+
+
+/**
+ * Retrieve the reference value from a Result Object.
+ *
+ * @param robject The result object to query.
+ *
+ * @return The reference associated with the request.
+ */
+#define fsl_shw_ro_get_reference(robject) \
+ (robject)->user_ref
+
+
+/**
+ * Retrieve the status code from a Result Object.
+ *
+ * @param robject The result object to query.
+ *
+ * @return The status of the request.
+ */
+#define fsl_shw_ro_get_status(robject) \
+ (robject)->code
+
+
+
+/* REQ-FSL-SHW-PINTFC-API-BASIC-HASH-004 */
+/**
+ * Initialize a Hash Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the current message length and hash algorithm in the hash
+ * context object.
+ *
+ * @param hcobject The hash context to operate upon.
+ * @param hcalgorithm The hash algorithm to be used (#FSL_HASH_ALG_MD5,
+ * #FSL_HASH_ALG_SHA256, etc).
+ *
+ */
+#define fsl_shw_hco_init(hcobject, hcalgorithm) \
+do { \
+ (hcobject)->algorithm = hcalgorithm; \
+ (hcobject)->flags = 0; \
+ switch (hcalgorithm) { \
+ case FSL_HASH_ALG_MD5: \
+ (hcobject)->digest_length = 16; \
+ (hcobject)->context_length = 16; \
+ (hcobject)->context_register_length = 24; \
+ break; \
+ case FSL_HASH_ALG_SHA1: \
+ (hcobject)->digest_length = 20; \
+ (hcobject)->context_length = 20; \
+ (hcobject)->context_register_length = 24; \
+ break; \
+ case FSL_HASH_ALG_SHA224: \
+ (hcobject)->digest_length = 28; \
+ (hcobject)->context_length = 32; \
+ (hcobject)->context_register_length = 36; \
+ break; \
+ case FSL_HASH_ALG_SHA256: \
+ (hcobject)->digest_length = 32; \
+ (hcobject)->context_length = 32; \
+ (hcobject)->context_register_length = 36; \
+ break; \
+ default: \
+ /* error ! */ \
+ (hcobject)->digest_length = 1; \
+ (hcobject)->context_length = 1; \
+ (hcobject)->context_register_length = 1; \
+ break; \
+ } \
+} while (0)
+
+
+/* REQ-FSL-SHW-PINTFC-API-BASIC-HASH-001 */
+/**
+ * Get the current hash value and message length from the hash context object.
+ *
+ * The algorithm must have already been specified. See #fsl_shw_hco_init().
+ *
+ * @param hcobject The hash context to query.
+ * @param[out] hccontext Pointer to the location of @a length octets where to
+ * store a copy of the current value of the digest.
+ * @param hcclength Number of octets of hash value to copy.
+ * @param[out] hcmsglen Pointer to the location to store the number of octets
+ * already hashed.
+ */
+#define fsl_shw_hco_get_digest(hcobject, hccontext, hcclength, hcmsglen) \
+do { \
+ memcpy(hccontext, (hcobject)->context, hcclength); \
+ if ((hcobject)->algorithm == FSL_HASH_ALG_SHA224 \
+ || (hcobject)->algorithm == FSL_HASH_ALG_SHA256) { \
+ *(hcmsglen) = (hcobject)->context[8]; \
+ } else { \
+ *(hcmsglen) = (hcobject)->context[5]; \
+ } \
+} while (0)
+
+
+/* REQ-FSL-SHW-PINTFC-API-BASIC-HASH-002 */
+/**
+ * Get the hash algorithm from the hash context object.
+ *
+ * @param hcobject The hash context to query.
+ * @param[out] hcalgorithm Pointer to where the algorithm is to be stored.
+ */
+#define fsl_shw_hco_get_info(hcobject, hcalgorithm) \
+do { \
+ *(hcalgorithm) = (hcobject)->algorithm; \
+} while (0)
+
+
+/* REQ-FSL-SHW-PINTFC-API-BASIC-HASH-003 */
+/* REQ-FSL-SHW-PINTFC-API-BASIC-HASH-004 */
+/**
+ * Set the current hash value and message length in the hash context object.
+ *
+ * The algorithm must have already been specified. See #fsl_shw_hco_init().
+ *
+ * @param hcobject The hash context to operate upon.
+ * @param hccontext Pointer to buffer of appropriate length to copy into
+ * the hash context object.
+ * @param hcmsglen The number of octets of the message which have
+ * already been hashed.
+ *
+ */
+#define fsl_shw_hco_set_digest(hcobject, hccontext, hcmsglen) \
+do { \
+ memcpy((hcobject)->context, hccontext, (hcobject)->context_length); \
+ if (((hcobject)->algorithm == FSL_HASH_ALG_SHA224) \
+ || ((hcobject)->algorithm == FSL_HASH_ALG_SHA256)) { \
+ (hcobject)->context[8] = hcmsglen; \
+ } else { \
+ (hcobject)->context[5] = hcmsglen; \
+ } \
+} while (0)
+
+
+/**
+ * Set flags in a Hash Context Object.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hcobject The hash context to be operated on.
+ * @param hcflags The flags to be set in the context. These can be ORed
+ * members of #fsl_shw_hash_ctx_flags_t.
+ */
+#define fsl_shw_hco_set_flags(hcobject, hcflags) \
+ (hcobject)->flags |= (hcflags)
+
+
+/**
+ * Clear flags in a Hash Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hcobject The hash context to be operated on.
+ * @param hcflags The flags to be reset in the context. These can be ORed
+ * members of #fsl_shw_hash_ctx_flags_t.
+ */
+#define fsl_shw_hco_clear_flags(hcobject, hcflags) \
+ (hcobject)->flags &= ~(hcflags)
+
+
+/**
+ * Initialize an HMAC Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the current message length and hash algorithm in the HMAC
+ * context object.
+ *
+ * @param hcobject The HMAC context to operate upon.
+ * @param hcalgorithm The hash algorithm to be used (#FSL_HASH_ALG_MD5,
+ * #FSL_HASH_ALG_SHA256, etc).
+ *
+ */
+#define fsl_shw_hmco_init(hcobject, hcalgorithm) \
+ fsl_shw_hco_init(hcobject, hcalgorithm)
+
+
+/**
+ * Set flags in an HMAC Context Object.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hcobject The HMAC context to be operated on.
+ * @param hcflags The flags to be set in the context. These can be ORed
+ * members of #fsl_shw_hmac_ctx_flags_t.
+ */
+#define fsl_shw_hmco_set_flags(hcobject, hcflags) \
+ (hcobject)->flags |= (hcflags)
+
+
+/**
+ * Clear flags in an HMAC Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hcobject The HMAC context to be operated on.
+ * @param hcflags The flags to be reset in the context. These can be ORed
+ * members of #fsl_shw_hmac_ctx_flags_t.
+ */
+#define fsl_shw_hmco_clear_flags(hcobject, hcflags) \
+ (hcobject)->flags &= ~(hcflags)
+
+
+/**
+ * Initialize a Symmetric Cipher Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. This will set the @a mode and @a algorithm and initialize the
+ * Object.
+ *
+ * @param scobject The context object to operate on.
+ * @param scalg The cipher algorithm this context will be used with.
+ * @param scmode #FSL_SYM_MODE_CBC, #FSL_SYM_MODE_ECB, etc.
+ *
+ */
+#define fsl_shw_scco_init(scobject, scalg, scmode) \
+do { \
+ register uint32_t bsb; /* block-size bytes */ \
+ \
+ switch (scalg) { \
+ case FSL_KEY_ALG_AES: \
+ bsb = 16; \
+ break; \
+ case FSL_KEY_ALG_DES: \
+ /* fall through */ \
+ case FSL_KEY_ALG_TDES: \
+ bsb = 8; \
+ break; \
+ case FSL_KEY_ALG_ARC4: \
+ bsb = 259; \
+ break; \
+ case FSL_KEY_ALG_HMAC: \
+ bsb = 1; /* meaningless */ \
+ break; \
+ default: \
+ bsb = 00; \
+ } \
+ (scobject)->block_size_bytes = bsb; \
+ (scobject)->mode = scmode; \
+ (scobject)->flags = 0; \
+} while (0)
+
+
+/**
+ * Set the flags for a Symmetric Cipher Context.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param scobject The context object to operate on.
+ * @param scflags The flags to reset (one or more values from
+ * #fsl_shw_sym_ctx_flags_t ORed together).
+ *
+ */
+#define fsl_shw_scco_set_flags(scobject, scflags) \
+ (scobject)->flags |= (scflags)
+
+
+/**
+ * Clear some flags in a Symmetric Cipher Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param scobject The context object to operate on.
+ * @param scflags The flags to reset (one or more values from
+ * #fsl_shw_sym_ctx_flags_t ORed together).
+ *
+ */
+#define fsl_shw_scco_clear_flags(scobject, scflags) \
+ (scobject)->flags &= ~(scflags)
+
+
+/**
+ * Set the Context (IV) for a Symmetric Cipher Context.
+ *
+ * This is to set the context/IV for #FSL_SYM_MODE_CBC mode, or to set the
+ * context (the S-Box and pointers) for ARC4. The full context size will
+ * be copied.
+ *
+ * @param scobject The context object to operate on.
+ * @param sccontext A pointer to the buffer which contains the context.
+ *
+ */
+#define fsl_shw_scco_set_context(scobject, sccontext) \
+ memcpy((scobject)->context, sccontext, \
+ (scobject)->block_size_bytes)
+
+
+/**
+ * Get the Context for a Symmetric Cipher Context.
+ *
+ * This is to retrieve the context/IV for #FSL_SYM_MODE_CBC mode, or to
+ * retrieve context (the S-Box and pointers) for ARC4. The full context
+ * will be copied.
+ *
+ * @param scobject The context object to operate on.
+ * @param[out] sccontext Pointer to location where context will be stored.
+ */
+#define fsl_shw_scco_get_context(scobject, sccontext) \
+ memcpy(sccontext, (scobject)->context, (scobject)->block_size_bytes)
+
+
+/**
+ * Set the Counter Value for a Symmetric Cipher Context.
+ *
+ * This will set the Counter Value for CTR mode.
+ *
+ * @param scobject The context object to operate on.
+ * @param sccounter The starting counter value. The number of octets.
+ * copied will be the block size for the algorithm.
+ * @param scmodulus The modulus for controlling the incrementing of the
+ * counter.
+ *
+ */
+#define fsl_shw_scco_set_counter_info(scobject, sccounter, scmodulus) \
+do { \
+ if ((sccounter) != NULL) { \
+ memcpy((scobject)->context, sccounter, \
+ (scobject)->block_size_bytes); \
+ } \
+ (scobject)->modulus_exp = scmodulus; \
+} while (0)
+
+
+/**
+ * Get the Counter Value for a Symmetric Cipher Context.
+ *
+ * This will retrieve the Counter Value is for CTR mode.
+ *
+ * @param scobject The context object to query.
+ * @param[out] sccounter Pointer to location to store the current counter
+ * value. The number of octets copied will be the
+ * block size for the algorithm.
+ * @param[out] scmodulus Pointer to location to store the modulus.
+ *
+ */
+#define fsl_shw_scco_get_counter_info(scobject, sccounter, scmodulus) \
+do { \
+ if ((sccounter) != NULL) { \
+ memcpy(sccounter, (scobject)->context, \
+ (scobject)->block_size_bytes); \
+ } \
+ if ((scmodulus) != NULL) { \
+ *(scmodulus) = (scobject)->modulus_exp; \
+ } \
+} while (0)
+
+
+/**
+ * Initialize a Authentication-Cipher Context.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acmode The mode for this object (only #FSL_ACC_MODE_CCM
+ * supported).
+ */
+#define fsl_shw_acco_init(acobject, acmode) \
+do { \
+ (acobject)->flags = 0; \
+ (acobject)->mode = (acmode); \
+} while (0)
+
+
+/**
+ * Set the flags for a Authentication-Cipher Context.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acflags The flags to set (one or more from
+ * #fsl_shw_auth_ctx_flags_t ORed together).
+ *
+ */
+#define fsl_shw_acco_set_flags(acobject, acflags) \
+ (acobject)->flags |= (acflags)
+
+
+/**
+ * Clear some flags in a Authentication-Cipher Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acflags The flags to reset (one or more from
+ * #fsl_shw_auth_ctx_flags_t ORed together).
+ *
+ */
+#define fsl_shw_acco_clear_flags(acobject, acflags) \
+ (acobject)->flags &= ~(acflags)
+
+
+/**
+ * Set up the Authentication-Cipher Object for CCM mode.
+ *
+ * This will set the @a auth_object for CCM mode and save the @a ctr,
+ * and @a mac_length. This function can be called instead of
+ * #fsl_shw_acco_init().
+ *
+ * The paramater @a ctr is Counter Block 0, (counter value 0), which is for the
+ * MAC.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acalg Cipher algorithm. Only AES is supported.
+ * @param accounter The initial counter value.
+ * @param acmaclen The number of octets used for the MAC. Valid values are
+ * 4, 6, 8, 10, 12, 14, and 16.
+ */
+/* Do we need to stash the +1 value of the CTR somewhere? */
+#define fsl_shw_acco_set_ccm(acobject, acalg, accounter, acmaclen) \
+ do { \
+ (acobject)->flags = 0; \
+ (acobject)->mode = FSL_ACC_MODE_CCM; \
+ (acobject)->auth_info.CCM_ctx_info.block_size_bytes = 16; \
+ (acobject)->cipher_ctx_info.block_size_bytes = 16; \
+ (acobject)->mac_length = acmaclen; \
+ fsl_shw_scco_set_counter_info(&(acobject)->cipher_ctx_info, accounter, \
+ FSL_CTR_MOD_128); \
+} while (0)
+
+
+/**
+ * Format the First Block (IV) & Initial Counter Value per NIST CCM.
+ *
+ * This function will also set the IV and CTR values per Appendix A of NIST
+ * Special Publication 800-38C (May 2004). It will also perform the
+ * #fsl_shw_acco_set_ccm() operation with information derived from this set of
+ * parameters.
+ *
+ * Note this function assumes the algorithm is AES. It initializes the
+ * @a auth_object by setting the mode to #FSL_ACC_MODE_CCM and setting the
+ * flags to be #FSL_ACCO_NIST_CCM.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param act The number of octets used for the MAC. Valid values are
+ * 4, 6, 8, 10, 12, 14, and 16.
+ * @param acad Number of octets of Associated Data (may be zero).
+ * @param acq A value for the size of the length of @a q field. Valid
+ * values are 1-8.
+ * @param acN The Nonce (packet number or other changing value). Must
+ * be (15 - @a q_length) octets long.
+ * @param acQ The value of Q (size of the payload in octets).
+ *
+ */
+#define fsl_shw_ccm_nist_format_ctr_and_iv(acobject, act, acad, acq, acN, acQ)\
+ do { \
+ uint64_t Q = acQ; \
+ uint8_t bflag = ((acad)?0x40:0) | ((((act)-2)/2)<<3) | ((acq)-1); \
+ unsigned i; \
+ uint8_t* qptr = (acobject)->auth_info.CCM_ctx_info.context + 15; \
+ (acobject)->auth_info.CCM_ctx_info.block_size_bytes = 16; \
+ (acobject)->cipher_ctx_info.block_size_bytes = 16; \
+ (acobject)->mode = FSL_ACC_MODE_CCM; \
+ (acobject)->flags = FSL_ACCO_NIST_CCM; \
+ \
+ /* Store away the MAC length (after calculating actual value */ \
+ (acobject)->mac_length = (act); \
+ /* Set Flag field in Block 0 */ \
+ *((acobject)->auth_info.CCM_ctx_info.context) = bflag; \
+ /* Set Nonce field in Block 0 */ \
+ memcpy((acobject)->auth_info.CCM_ctx_info.context+1, acN, \
+ 15-(acq)); \
+ /* Set Flag field in ctr */ \
+ *((acobject)->cipher_ctx_info.context) = (acq)-1; \
+ /* Update the Q (payload length) field of Block0 */ \
+ (acobject)->q_length = acq; \
+ for (i = 0; i < (acq); i++) { \
+ *qptr-- = Q & 0xFF; \
+ Q >>= 8; \
+ } \
+ /* Set the Nonce field of the ctr */ \
+ memcpy((acobject)->cipher_ctx_info.context+1, acN, 15-(acq)); \
+ /* Clear the block counter field of the ctr */ \
+ memset((acobject)->cipher_ctx_info.context+16-(acq), 0, (acq)+1); \
+ } while (0)
+
+
+/**
+ * Update the First Block (IV) & Initial Counter Value per NIST CCM.
+ *
+ * This function will set the IV and CTR values per Appendix A of NIST Special
+ * Publication 800-38C (May 2004).
+ *
+ * Note this function assumes that #fsl_shw_ccm_nist_format_ctr_and_iv() has
+ * previously been called on the @a auth_object.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acN The Nonce (packet number or other changing value). Must
+ * be (15 - @a q_length) octets long.
+ * @param acQ The value of Q (size of the payload in octets).
+ *
+ */
+/* Do we need to stash the +1 value of the CTR somewhere? */
+#define fsl_shw_ccm_nist_update_ctr_and_iv(acobject, acN, acQ) \
+ do { \
+ uint64_t Q = acQ; \
+ unsigned i; \
+ uint8_t* qptr = (acobject)->auth_info.CCM_ctx_info.context + 15; \
+ \
+ /* Update the Nonce field field of Block0 */ \
+ memcpy((acobject)->auth_info.CCM_ctx_info.context+1, acN, \
+ 15 - (acobject)->q_length); \
+ /* Update the Q (payload length) field of Block0 */ \
+ for (i = 0; i < (acobject)->q_length; i++) { \
+ *qptr-- = Q & 0xFF; \
+ Q >>= 8; \
+ } \
+ /* Update the Nonce field of the ctr */ \
+ memcpy((acobject)->cipher_ctx_info.context+1, acN, \
+ 15 - (acobject)->q_length); \
+ } while (0)
+
+
+/******************************************************************************
+ * Library functions
+ *****************************************************************************/
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSLSHW-PINTFC-API-GEN-003 */
+/**
+ * Determine the hardware security capabilities of this platform.
+ *
+ * Though a user context object is passed into this function, it will always
+ * act in a non-blocking manner.
+ *
+ * @param user_ctx The user context which will be used for the query.
+ *
+ * @return A pointer to the capabilities object.
+ */
+extern fsl_shw_pco_t* fsl_shw_get_capabilities(fsl_shw_uco_t* user_ctx);
+
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSLSHW-PINTFC-API-GEN-004 */
+/**
+ * Create an association between the the user and the provider of the API.
+ *
+ * @param user_ctx The user context which will be used for this association.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_register_user(fsl_shw_uco_t* user_ctx);
+
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSLSHW-PINTFC-API-GEN-005 */
+/**
+ * Destroy the association between the the user and the provider of the API.
+ *
+ * @param user_ctx The user context which is no longer needed.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_deregister_user(fsl_shw_uco_t* user_ctx);
+
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSLSHW-PINTFC-API-GEN-006 */
+/**
+ * Retrieve results from earlier operations.
+ *
+ * @param user_ctx The user's context.
+ * @param result_size The number of array elements of @a results.
+ * @param[in,out] results Pointer to first of the (array of) locations to
+ * store results.
+ * @param[out] result_count Pointer to store the number of results which
+ * were returned.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_get_results(fsl_shw_uco_t* user_ctx,
+ unsigned result_size,
+ fsl_shw_result_t results[],
+ unsigned* result_count);
+
+/**
+ * Place a key into a protected location for use only by cryptographic
+ * algorithms.
+ *
+ * This only needs to be used to a) unwrap a key, or b) set up a key which
+ * could be wrapped with a later call to #fsl_shw_extract_key(). Normal
+ * cleartext keys can simply be placed into #fsl_shw_sko_t key objects with
+ * #fsl_shw_sko_set_key() and used directly.
+ *
+ * The maximum key size supported for wrapped/unwrapped keys is 32 octets.
+ * (This is the maximum reasonable key length on Sahara - 32 octets for an HMAC
+ * key based on SHA-256.) The key size is determined by the @a key_info. The
+ * expected length of @a key can be determined by
+ * #fsl_shw_sko_calculate_wrapped_size()
+ *
+ * The protected key will not be available for use until this operation
+ * successfully completes.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] key_info The information about the key to be which will
+ * be established. In the create case, the key
+ * length must be set.
+ * @param establish_type How @a key will be interpreted to establish a
+ * key for use.
+ * @param key If @a establish_type is #FSL_KEY_WRAP_UNWRAP,
+ * this is the location of a wrapped key. If
+ * @a establish_type is #FSL_KEY_WRAP_CREATE, this
+ * parameter can be @a NULL. If @a establish_type
+ * is #FSL_KEY_WRAP_ACCEPT, this is the location
+ * of a plaintext key.
+ */
+extern fsl_shw_return_t fsl_shw_establish_key(
+ fsl_shw_uco_t* user_ctx,
+ fsl_shw_sko_t* key_info,
+ fsl_shw_key_wrap_t establish_type,
+ const uint8_t* key);
+
+
+/**
+ * Wrap a key and retrieve the wrapped value.
+ *
+ * A wrapped key is a key that has been cryptographically obscured. It is
+ * only able to be used with #fsl_shw_establish_key().
+ *
+ * This function will also release the key (see #fsl_shw_release_key()) so
+ * that it must be re-established before reuse.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The information about the key to be deleted.
+ * @param[out] covered_key The location to store the wrapped key.
+ * (This size is based upon the maximum key size
+ * of 32 octets).
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_extract_key(fsl_shw_uco_t* user_ctx,
+ fsl_shw_sko_t* key_info,
+ uint8_t* covered_key);
+
+/*!
+ * Read the key value from a key object.
+ *
+ * Only a key marked as a software key (#FSL_SKO_KEY_SW_KEY) can be read with
+ * this call. It has no effect on the status of the key store.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The referenced key.
+ * @param[out] key The location to store the key value.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_read_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ uint8_t * key);
+
+/**
+ * De-establish a key so that it can no longer be accessed.
+ *
+ * The key will need to be re-established before it can again be used.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The information about the key to be deleted.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_release_key(
+ fsl_shw_uco_t* user_ctx,
+ fsl_shw_sko_t* key_info);
+
+
+/*
+ * In userspace, partition assignments will be tracked using the user context.
+ * In kernel mode, partition assignments are based on address only.
+ */
+
+/**
+ * Allocate a block of secure memory
+ *
+ * @param user_ctx User context
+ * @param size Memory size (octets). Note: currently only
+ * supports only single-partition sized blocks.
+ * @param UMID User Mode ID to use when registering the
+ * partition.
+ * @param permissions Permissions to initialize the partition with.
+ * Can be made by ORing flags from the
+ * #fsl_shw_permission_t.
+ *
+ * @return Address of the allocated memory. NULL if the
+ * call was not successful.
+ */
+extern void *fsl_shw_smalloc(fsl_shw_uco_t* user_ctx,
+ uint32_t size,
+ const uint8_t* UMID,
+ uint32_t permissions);
+
+
+/**
+ * Free a block of secure memory that was allocated with #fsl_shw_smalloc
+ *
+ * @param user_ctx User context
+ * @param address Address of the block of secure memory to be
+ * released.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_sfree(
+ fsl_shw_uco_t* user_ctx,
+ void* address);
+
+
+/**
+ * Check the status of a block of a secure memory that was allocated with
+ * #fsl_shw_smalloc
+ *
+ * @param user_ctx User context
+ * @param address Address of the block of secure memory to be
+ * released.
+ * @param status Status of the partition, of type
+ * #fsl_partition_status_t
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_sstatus(fsl_shw_uco_t* user_ctx,
+ void* address,
+ fsl_shw_partition_status_t* status);
+
+
+/**
+ * Diminish the permissions of a block of secure memory. Note that permissions
+ * can only be revoked.
+ *
+ * @param user_ctx User context
+ * @param address Base address of the secure memory to work with
+ * @param permissions Permissions to initialize the partition with.
+ * Can be made by ORing flags from the
+ * #fsl_shw_permission_t.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_diminish_perms(
+ fsl_shw_uco_t* user_ctx,
+ void* address,
+ uint32_t permissions);
+
+extern fsl_shw_return_t do_scc_engage_partition(
+ fsl_shw_uco_t* user_ctx,
+ void* address,
+ const uint8_t* UMID,
+ uint32_t permissions);
+
+extern fsl_shw_return_t do_system_keystore_slot_alloc(
+ fsl_shw_uco_t* user_ctx,
+ uint32_t key_lenth,
+ uint64_t ownerid,
+ uint32_t *slot);
+
+extern fsl_shw_return_t do_system_keystore_slot_dealloc(
+ fsl_shw_uco_t* user_ctx,
+ uint64_t ownerid,
+ uint32_t slot);
+
+extern fsl_shw_return_t do_system_keystore_slot_load(
+ fsl_shw_uco_t* user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ const uint8_t *key,
+ uint32_t key_length);
+
+extern fsl_shw_return_t do_system_keystore_slot_encrypt(
+ fsl_shw_uco_t* user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ uint8_t* black_data);
+
+extern fsl_shw_return_t do_system_keystore_slot_decrypt(
+ fsl_shw_uco_t* user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ const uint8_t* black_data);
+
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSL-SHW-PINTFC-COA-SKO */
+/* REQ-FSL-SHW-PINTFC-COA-SCCO */
+/* REQ-FSLSHW-PINTFC-API-BASIC-SYM-001 */
+/* PINTFC-API-BASIC-SYM-ARC4-001 */
+/* PINTFC-API-BASIC-SYM-ARC4-002 */
+/**
+ * Encrypt a stream of data with a symmetric-key algorithm.
+ *
+ * In ARC4, and also in #FSL_SYM_MODE_CBC and #FSL_SYM_MODE_CTR modes, the
+ * flags of the @a sym_ctx object will control part of the operation of this
+ * function. The #FSL_SYM_CTX_INIT flag means that there is no context info in
+ * the object. The #FSL_SYM_CTX_LOAD means to use information in the
+ * @a sym_ctx at the start of the operation, and the #FSL_SYM_CTX_SAVE flag
+ * means to update the object's context information after the operation has
+ * been performed.
+ *
+ * All of the data for an operation can be run through at once using the
+ * #FSL_SYM_CTX_INIT or #FSL_SYM_CTX_LOAD flags, as appropriate, and then using
+ * a @a length for the whole of the data.
+ *
+ * If a #FSL_SYM_CTX_SAVE flag were added, an additional call to the function
+ * would "pick up" where the previous call left off, allowing the user to
+ * perform the larger function in smaller steps.
+ *
+ * In #FSL_SYM_MODE_CBC and #FSL_SYM_MODE_ECB modes, the @a length must always
+ * be a multiple of the block size for the algorithm being used. For proper
+ * operation in #FSL_SYM_MODE_CTR mode, the @a length must be a multiple of the
+ * block size until the last operation on the total octet stream.
+ *
+ * Some users of ARC4 may want to compute the context (S-Box and pointers) from
+ * the key before any data is available. This may be done by running this
+ * function with a @a length of zero, with the init & save flags flags on in
+ * the @a sym_ctx. Subsequent operations would then run as normal with the
+ * load and save flags. Note that they key object is still required.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info Key and algorithm being used for this operation.
+ * @param[in,out] sym_ctx Info on cipher mode, state of the cipher.
+ * @param length Length, in octets, of the pt (and ct).
+ * @param pt pointer to plaintext to be encrypted.
+ * @param[out] ct pointer to where to store the resulting ciphertext.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ *
+ */
+extern fsl_shw_return_t fsl_shw_symmetric_encrypt(
+ fsl_shw_uco_t* user_ctx,
+ fsl_shw_sko_t* key_info,
+ fsl_shw_scco_t* sym_ctx,
+ uint32_t length,
+ const uint8_t* pt,
+ uint8_t* ct);
+
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSL-SHW-PINTFC-COA-SKO */
+/* REQ-FSL-SHW-PINTFC-COA-SCCO */
+/* PINTFC-API-BASIC-SYM-002 */
+/* PINTFC-API-BASIC-SYM-ARC4-001 */
+/* PINTFC-API-BASIC-SYM-ARC4-002 */
+/**
+ * Decrypt a stream of data with a symmetric-key algorithm.
+ *
+ * In ARC4, and also in #FSL_SYM_MODE_CBC and #FSL_SYM_MODE_CTR modes, the
+ * flags of the @a sym_ctx object will control part of the operation of this
+ * function. The #FSL_SYM_CTX_INIT flag means that there is no context info in
+ * the object. The #FSL_SYM_CTX_LOAD means to use information in the
+ * @a sym_ctx at the start of the operation, and the #FSL_SYM_CTX_SAVE flag
+ * means to update the object's context information after the operation has
+ * been performed.
+ *
+ * All of the data for an operation can be run through at once using the
+ * #FSL_SYM_CTX_INIT or #FSL_SYM_CTX_LOAD flags, as appropriate, and then using
+ * a @a length for the whole of the data.
+ *
+ * If a #FSL_SYM_CTX_SAVE flag were added, an additional call to the function
+ * would "pick up" where the previous call left off, allowing the user to
+ * perform the larger function in smaller steps.
+ *
+ * In #FSL_SYM_MODE_CBC and #FSL_SYM_MODE_ECB modes, the @a length must always
+ * be a multiple of the block size for the algorithm being used. For proper
+ * operation in #FSL_SYM_MODE_CTR mode, the @a length must be a multiple of the
+ * block size until the last operation on the total octet stream.
+ *
+ * Some users of ARC4 may want to compute the context (S-Box and pointers) from
+ * the key before any data is available. This may be done by running this
+ * function with a @a length of zero, with the #FSL_SYM_CTX_INIT &
+ * #FSL_SYM_CTX_SAVE flags on in the @a sym_ctx. Subsequent operations would
+ * then run as normal with the load & save flags. Note that they key object is
+ * still required.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The key and algorithm being used in this operation.
+ * @param[in,out] sym_ctx Info on cipher mode, state of the cipher.
+ * @param length Length, in octets, of the ct (and pt).
+ * @param ct pointer to ciphertext to be decrypted.
+ * @param[out] pt pointer to where to store the resulting plaintext.
+ *
+ * @return A return code of type #fsl_shw_return_t
+ *
+ */
+extern fsl_shw_return_t fsl_shw_symmetric_decrypt(
+ fsl_shw_uco_t* user_ctx,
+ fsl_shw_sko_t* key_info,
+ fsl_shw_scco_t* sym_ctx,
+ uint32_t length,
+ const uint8_t* ct,
+ uint8_t* pt);
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSL-SHW-PINTFC-COA-HCO */
+/* REQ-FSLSHW-PINTFC-API-BASIC-HASH-005 */
+/**
+ * Hash a stream of data with a cryptographic hash algorithm.
+ *
+ * The flags in the @a hash_ctx control the operation of this function.
+ *
+ * Hashing functions work on 64 octets of message at a time. Therefore, when
+ * any partial hashing of a long message is performed, the message @a length of
+ * each segment must be a multiple of 64. When ready to
+ * #FSL_HASH_FLAGS_FINALIZE the hash, the @a length may be any value.
+ *
+ * With the #FSL_HASH_FLAGS_INIT and #FSL_HASH_FLAGS_FINALIZE flags on, a
+ * one-shot complete hash, including padding, will be performed. The @a length
+ * may be any value.
+ *
+ * The first octets of a data stream can be hashed by setting the
+ * #FSL_HASH_FLAGS_INIT and #FSL_HASH_FLAGS_SAVE flags. The @a length must be
+ * a multiple of 64.
+ *
+ * The flag #FSL_HASH_FLAGS_LOAD is used to load a context previously saved by
+ * #FSL_HASH_FLAGS_SAVE. The two in combination will allow a (multiple-of-64
+ * octets) 'middle sequence' of the data stream to be hashed with the
+ * beginning. The @a length must again be a multiple of 64.
+ *
+ * Since the flag #FSL_HASH_FLAGS_LOAD is used to load a context previously
+ * saved by #FSL_HASH_FLAGS_SAVE, the #FSL_HASH_FLAGS_LOAD and
+ * #FSL_HASH_FLAGS_FINALIZE flags, used together, can be used to finish the
+ * stream. The @a length may be any value.
+ *
+ * If the user program wants to do the padding for the hash, it can leave off
+ * the #FSL_HASH_FLAGS_FINALIZE flag. The @a length must then be a multiple of
+ * 64 octets.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] hash_ctx Hashing algorithm and state of the cipher.
+ * @param msg Pointer to the data to be hashed.
+ * @param length Length, in octets, of the @a msg.
+ * @param[out] result If not null, pointer to where to store the hash
+ * digest.
+ * @param result_len Number of octets to store in @a result.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_hash(
+ fsl_shw_uco_t* user_ctx,
+ fsl_shw_hco_t* hash_ctx,
+ const uint8_t* msg,
+ uint32_t length,
+ uint8_t* result,
+ uint32_t result_len);
+
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSL-SHW-PINTFC-API-BASIC-HMAC-001 */
+/**
+ * Precompute the Key hashes for an HMAC operation.
+ *
+ * This function may be used to calculate the inner and outer precomputes,
+ * which are the hash contexts resulting from hashing the XORed key for the
+ * 'inner hash' and the 'outer hash', respectively, of the HMAC function.
+ *
+ * After execution of this function, the @a hmac_ctx will contain the
+ * precomputed inner and outer contexts, so that they may be used by
+ * #fsl_shw_hmac(). The flags of @a hmac_ctx will be updated with
+ * #FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT to mark their presence. In addtion, the
+ * #FSL_HMAC_FLAGS_INIT flag will be set.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The key being used in this operation. Key must be
+ * 1 to 64 octets long.
+ * @param[in,out] hmac_ctx The context which controls, by its flags and
+ * algorithm, the operation of this function.
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_hmac_precompute(
+ fsl_shw_uco_t* user_ctx,
+ fsl_shw_sko_t* key_info,
+ fsl_shw_hmco_t* hmac_ctx);
+
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSLSHW-PINTFC-API-BASIC-HMAC-002 */
+/**
+ * Continue, finalize, or one-shot an HMAC operation.
+ *
+ * There are a number of ways to use this function. The flags in the
+ * @a hmac_ctx object will determine what operations occur.
+ *
+ * If #FSL_HMAC_FLAGS_INIT is set, then the hash will be started either from
+ * the @a key_info, or from the precomputed inner hash value in the
+ * @a hmac_ctx, depending on the value of #FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT.
+ *
+ * If, instead, #FSL_HMAC_FLAGS_LOAD is set, then the hash will be continued
+ * from the ongoing inner hash computation in the @a hmac_ctx.
+ *
+ * If #FSL_HMAC_FLAGS_FINALIZE are set, then the @a msg will be padded, hashed,
+ * the outer hash will be performed, and the @a result will be generated.
+ *
+ * If the #FSL_HMAC_FLAGS_SAVE flag is set, then the (ongoing or final) digest
+ * value will be stored in the ongoing inner hash computation field of the @a
+ * hmac_ctx.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info If #FSL_HMAC_FLAGS_INIT is set in the @a hmac_ctx,
+ * this is the key being used in this operation, and the
+ * IPAD. If #FSL_HMAC_FLAGS_INIT is set in the @a
+ * hmac_ctx and @a key_info is NULL, then
+ * #fsl_shw_hmac_precompute() has been used to populate
+ * the @a inner_precompute and @a outer_precompute
+ * contexts. If #FSL_HMAC_FLAGS_INIT is not set, this
+ * parameter is ignored.
+
+ * @param[in,out] hmac_ctx The context which controls, by its flags and
+ * algorithm, the operation of this function.
+ * @param msg Pointer to the message to be hashed.
+ * @param length Length, in octets, of the @a msg.
+ * @param[out] result Pointer, of @a result_len octets, to where to
+ * store the HMAC.
+ * @param result_len Length of @a result buffer.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_hmac(
+ fsl_shw_uco_t* user_ctx,
+ fsl_shw_sko_t* key_info,
+ fsl_shw_hmco_t* hmac_ctx,
+ const uint8_t* msg,
+ uint32_t length,
+ uint8_t* result,
+ uint32_t result_len);
+
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSLSHW-PINTFC-API-BASIC-RNG-002 */
+/**
+ * Get random data.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param length The number of octets of @a data being requested.
+ * @param[out] data A pointer to a location of @a length octets to where
+ * random data will be returned.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_get_random(
+ fsl_shw_uco_t* user_ctx,
+ uint32_t length,
+ uint8_t* data);
+
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSLSHW-PINTFC-API-BASIC-RNG-003 */
+/**
+ * Add entropy to random number generator.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param length Number of bytes at @a data.
+ * @param data Entropy to add to random number generator.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_add_entropy(
+ fsl_shw_uco_t* user_ctx,
+ uint32_t length,
+ uint8_t* data);
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSL-SHW-PINTFC-COA-SKO */
+/**
+ * Perform Generation-Encryption by doing a Cipher and a Hash.
+ *
+ * Generate the authentication value @a auth_value as well as encrypt the @a
+ * payload into @a ct (the ciphertext). This is a one-shot function, so all of
+ * the @a auth_data and the total message @a payload must passed in one call.
+ * This also means that the flags in the @a auth_ctx must be #FSL_ACCO_CTX_INIT
+ * and #FSL_ACCO_CTX_FINALIZE.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param auth_ctx Controlling object for Authenticate-decrypt.
+ * @param cipher_key_info The key being used for the cipher part of this
+ * operation. In CCM mode, this key is used for
+ * both parts.
+ * @param auth_key_info The key being used for the authentication part
+ * of this operation. In CCM mode, this key is
+ * ignored and may be NULL.
+ * @param auth_data_length Length, in octets, of @a auth_data.
+ * @param auth_data Data to be authenticated but not encrypted.
+ * @param payload_length Length, in octets, of @a payload.
+ * @param payload Pointer to the plaintext to be encrypted.
+ * @param[out] ct Pointer to the where the encrypted @a payload
+ * will be stored. Must be @a payload_length
+ * octets long.
+ * @param[out] auth_value Pointer to where the generated authentication
+ * field will be stored. Must be as many octets as
+ * indicated by MAC length in the @a function_ctx.
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_gen_encrypt(
+ fsl_shw_uco_t* user_ctx,
+ fsl_shw_acco_t* auth_ctx,
+ fsl_shw_sko_t* cipher_key_info,
+ fsl_shw_sko_t* auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t* auth_data,
+ uint32_t payload_length,
+ const uint8_t* payload,
+ uint8_t* ct,
+ uint8_t* auth_value);
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSL-SHW-PINTFC-COA-SKO */
+/**
+ * Perform Authentication-Decryption in Cipher + Hash.
+ *
+ * This function will perform a one-shot decryption of a data stream as well as
+ * authenticate the authentication value. This is a one-shot function, so all
+ * of the @a auth_data and the total message @a payload must passed in one
+ * call. This also means that the flags in the @a auth_ctx must be
+ * #FSL_ACCO_CTX_INIT and #FSL_ACCO_CTX_FINALIZE.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param auth_ctx Controlling object for Authenticate-decrypt.
+ * @param cipher_key_info The key being used for the cipher part of this
+ * operation. In CCM mode, this key is used for
+ * both parts.
+ * @param auth_key_info The key being used for the authentication part
+ * of this operation. In CCM mode, this key is
+ * ignored and may be NULL.
+ * @param auth_data_length Length, in octets, of @a auth_data.
+ * @param auth_data Data to be authenticated but not decrypted.
+ * @param payload_length Length, in octets, of @a ct and @a pt.
+ * @param ct Pointer to the encrypted input stream.
+ * @param auth_value The (encrypted) authentication value which will
+ * be authenticated. This is the same data as the
+ * (output) @a auth_value argument to
+ * #fsl_shw_gen_encrypt().
+ * @param[out] payload Pointer to where the plaintext resulting from
+ * the decryption will be stored.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_auth_decrypt(
+ fsl_shw_uco_t* user_ctx,
+ fsl_shw_acco_t* auth_ctx,
+ fsl_shw_sko_t* cipher_key_info,
+ fsl_shw_sko_t* auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t* auth_data,
+ uint32_t payload_length,
+ const uint8_t* ct,
+ const uint8_t* auth_value,
+ uint8_t* payload);
+
+/*!
+ * Cause the hardware to create a new random key for secure memory use.
+ *
+ * Have the hardware use the secure hardware random number generator to load a
+ * new secret key into the hardware random key register. It will not be made
+ * active without a call to #fsl_shw_select_pf_key().
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+#ifdef __KERNEL__
+
+extern fsl_shw_return_t fsl_shw_gen_random_pf_key(fsl_shw_uco_t * user_ctx);
+
+#else
+
+#define fsl_shw_gen_random_pf_key(user_ctx) FSL_RETURN_NO_RESOURCE_S
+
+#endif /* __KERNEL__ */
+
+/*!
+ * Retrieve the detected tamper event.
+ *
+ * Note that if more than one event was detected, this routine will only ever
+ * return one of them.
+ *
+ * @param[in] user_ctx A user context from #fsl_shw_register_user().
+ * @param[out] tamperp Location to store the tamper information.
+ * @param[out] timestampp Locate to store timestamp from hardwhare when
+ * an event was detected.
+ *
+ *
+ * @return A return code of type #fsl_shw_return_t (for instance, if the platform
+ * is not in a fail state.
+ */
+#ifdef __KERNEL__
+
+extern fsl_shw_return_t fsl_shw_read_tamper_event(fsl_shw_uco_t * user_ctx,
+ fsl_shw_tamper_t * tamperp,
+ uint64_t * timestampp);
+#else
+
+#define fsl_shw_read_tamper_event(user_ctx,tamperp,timestampp) \
+ FSL_RETURN_NO_RESOURCE_S
+
+#endif /* __KERNEL__ */
+
+/*****************************************************************************
+ *
+ * Functions internal to SHW driver.
+ *
+*****************************************************************************/
+
+fsl_shw_return_t
+do_scc_encrypt_region(fsl_shw_uco_t* user_ctx,
+ void* partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, uint8_t* black_data,
+ uint32_t* IV, fsl_shw_cypher_mode_t cypher_mode);
+
+fsl_shw_return_t
+do_scc_decrypt_region(fsl_shw_uco_t* user_ctx,
+ void* partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, const uint8_t* black_data,
+ uint32_t* IV, fsl_shw_cypher_mode_t cypher_mode);
+
+
+/*****************************************************************************
+ *
+ * Functions available to other SHW-family drivers.
+ *
+*****************************************************************************/
+
+#ifdef __KERNEL__
+/**
+ * Add an entry to a work/result queue.
+ *
+ * @param pool Pointer to list structure
+ * @param entry Entry to place at tail of list
+ *
+ * @return void
+ */
+inline static void SHW_ADD_QUEUE_ENTRY(shw_queue_t* pool,
+ shw_queue_entry_t* entry)
+{
+ os_lock_context_t lock_context;
+
+ entry->next = NULL;
+ os_lock_save_context(shw_queue_lock, lock_context);
+
+ if (pool->tail != NULL) {
+ pool->tail->next = entry;
+ } else {
+ /* Queue was empty, so this is also the head. */
+ pool->head = entry;
+ }
+ pool->tail = entry;
+
+ os_unlock_restore_context(shw_queue_lock, lock_context);
+
+ return;
+
+
+}
+
+
+/**
+ * Get first entry on the queue and remove it from the queue.
+ *
+ * @return Pointer to first entry, or NULL if none.
+ */
+inline static shw_queue_entry_t* SHW_POP_FIRST_ENTRY(shw_queue_t* queue)
+{
+ shw_queue_entry_t* entry;
+ os_lock_context_t lock_context;
+
+ os_lock_save_context(shw_queue_lock, lock_context);
+
+ entry = queue->head;
+
+ if (entry != NULL) {
+ queue->head = entry->next;
+ entry->next = NULL;
+ /* If this was only entry, clear the tail. */
+ if (queue->tail == entry) {
+ queue->tail = NULL;
+ }
+ }
+
+ os_unlock_restore_context(shw_queue_lock, lock_context);
+
+ return entry;
+}
+
+
+
+/**
+ * Remove an entry from the list.
+ *
+ * If the entry not on the queue, no error will be returned.
+ *
+ * @param pool Pointer to work queue
+ * @param entry Entry to remove from queue
+ *
+ * @return void
+ *
+ */
+inline static void SHW_QUEUE_REMOVE_ENTRY(shw_queue_t* pool,
+ shw_queue_entry_t* entry)
+{
+ os_lock_context_t lock_context;
+
+ os_lock_save_context(shw_queue_lock, lock_context);
+
+ /* Check for quick case.*/
+ if (pool->head == entry) {
+ pool->head = entry->next;
+ entry->next = NULL;
+ if (pool->tail == entry) {
+ pool->tail = NULL;
+ }
+ } else {
+ register shw_queue_entry_t* prev = pool->head;
+
+ /* We know it is not the head, so start looking at entry after head. */
+ while (prev->next) {
+ if (prev->next != entry) {
+ prev = prev->next; /* Try another */
+ continue;
+ } else {
+ /* Unlink from chain. */
+ prev->next = entry->next;
+ entry->next = NULL;
+ /* If last in chain, update tail. */
+ if (pool->tail == entry) {
+ pool->tail = prev;
+ }
+ break;
+ }
+ } /* while */
+ }
+
+ os_unlock_restore_context(shw_queue_lock, lock_context);
+
+ return;
+}
+#endif /* __KERNEL__ */
+
+
+/*****************************************************************************
+ *
+ * Functions available to User-Mode API functions
+ *
+ ****************************************************************************/
+#ifndef __KERNEL__
+
+
+ /**
+ * Sanity checks the user context object fields to ensure that they make some
+ * sense before passing the uco as a parameter.
+ *
+ * @brief Verify the user context object
+ *
+ * @param uco user context object
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t validate_uco(fsl_shw_uco_t *uco);
+
+
+/**
+ * Initialize a request block to go to the driver.
+ *
+ * @param hdr Pointer to request block header
+ * @param user_ctx Pointer to user's context
+ *
+ * @return void
+ */
+inline static void init_req(struct shw_req_header* hdr,
+ fsl_shw_uco_t* user_ctx)
+{
+ hdr->flags = user_ctx->flags;
+ hdr->user_ref = user_ctx->user_ref;
+ hdr->code = FSL_RETURN_ERROR_S;
+
+ return;
+}
+
+
+/**
+ * Send a request block off to the driver.
+ *
+ * If this is a non-blocking request, then req will be freed.
+ *
+ * @param type The type of request being sent
+ * @param req Pointer to the request block
+ * @param ctx Pointer to user's context
+ *
+ * @return code from driver if ioctl() succeeded, otherwise
+ * FSL_RETURN_INTERNAL_ERROR_S.
+ */
+inline static fsl_shw_return_t send_req(shw_user_request_t type,
+ struct shw_req_header* req,
+ fsl_shw_uco_t* ctx)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+ unsigned blocking = ctx->flags & FSL_UCO_BLOCKING_MODE;
+ int code;
+
+ code = ioctl(ctx->openfd, SHW_IOCTL_REQUEST + type, req);
+
+ if (code == 0) {
+ if (blocking) {
+ ret = req->code;
+ } else {
+ ret = FSL_RETURN_OK_S;
+ }
+ } else {
+#ifdef FSL_DEBUG
+ fprintf(stderr, "SHW: send_req failed with (%d), %s\n", errno,
+ strerror(errno));
+#endif
+ }
+
+ if (blocking) {
+ free(req);
+ }
+
+ return ret;
+}
+
+
+#endif /* no __KERNEL__ */
+
+#if defined(FSL_HAVE_DRYICE)
+/* Some kernel functions */
+void fsl_shw_permute1_bytes(const uint8_t * key, uint8_t * permuted_key,
+ int key_count);
+void fsl_shw_permute1_bytes_to_words(const uint8_t * key,
+ uint32_t * permuted_key, int key_count);
+
+#define PFKEY_TO_STR(key_in) \
+({ \
+ di_key_t key = key_in; \
+ \
+ ((key == DI_KEY_FK) ? "IIM" : \
+ ((key == DI_KEY_PK) ? "PRG" : \
+ ((key == DI_KEY_RK) ? "RND" : \
+ ((key == DI_KEY_FPK) ? "IIM_PRG" : \
+ ((key == DI_KEY_FRK) ? "IIM_RND" : "unk"))))); \
+})
+
+#ifdef DIAG_SECURITY_FUNC
+extern const char *di_error_string(int code);
+#endif
+
+#endif /* HAVE DRYICE */
+
+#endif /* SHW_DRIVER_H */
diff --git a/drivers/mxc/security/rng/include/shw_hash.h b/drivers/mxc/security/rng/include/shw_hash.h
new file mode 100644
index 000000000000..8026c7ecb3fd
--- /dev/null
+++ b/drivers/mxc/security/rng/include/shw_hash.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+/*!
+ * @file shw_hash.h
+ *
+ * This file contains definitions for use of the (internal) SHW hash
+ * software computation. It defines the usual three steps:
+ *
+ * - #shw_hash_init()
+ * - #shw_hash_update()
+ * - #shw_hash_final()
+ *
+ * The only other item of note to callers is #SHW_HASH_LEN, which is the number
+ * of bytes calculated for the hash.
+ */
+
+#ifndef SHW_HASH_H
+#define SHW_HASH_H
+
+/*! Define which gives the number of bytes available in an hash result */
+#define SHW_HASH_LEN 32
+
+/* Define which matches block length in bytes of the underlying hash */
+#define SHW_HASH_BLOCK_LEN 64
+
+/* "Internal" define which matches SHA-256 state size (32-bit words) */
+#define SHW_HASH_STATE_WORDS 8
+
+/* "Internal" define which matches word length in blocks of the underlying
+ hash. */
+#define SHW_HASH_BLOCK_WORD_SIZE 16
+
+#define SHW_HASH_STATE_SIZE 32
+
+/*!
+ * State for a SHA-1/SHA-2 Hash
+ *
+ * (Note to maintainers: state needs to be updated to uint64_t to handle
+ * SHA-384/SHA-512)... And bit_count to uint128_t (heh).
+ */
+typedef struct shw_hash_state {
+ unsigned int partial_count_bytes; /*!< Number of bytes of message sitting
+ * in @c partial_block */
+ uint8_t partial_block[SHW_HASH_BLOCK_LEN]; /*!< Data waiting to be processed as a block */
+ uint32_t state[SHW_HASH_STATE_WORDS]; /*!< Current hash state variables */
+ uint64_t bit_count; /*!< Number of bits sent through the update function */
+} shw_hash_state_t;
+
+/*!
+ * Initialize the hash state structure
+ *
+ * @param state Address of hash state structure.
+ * @param algorithm Which hash algorithm to use (must be FSL_HASH_ALG_SHA256)
+ *
+ * @return FSL_RETURN_OK_S if all went well, otherwise an error code.
+ */
+fsl_shw_return_t shw_hash_init(shw_hash_state_t * state,
+ fsl_shw_hash_alg_t algorithm);
+
+/*!
+ * Put data into the hash calculation
+ *
+ * @param state Address of hash state structure.
+ * @param msg Address of the message data for the hash.
+ * @param msg_len Number of bytes of @c msg.
+ *
+ * @return FSL_RETURN_OK_S if all went well, otherwise an error code.
+ */
+fsl_shw_return_t shw_hash_update(shw_hash_state_t * state,
+ const uint8_t * msg, unsigned int msg_len);
+
+/*!
+ * Calculate the final hash value
+ *
+ * @param state Address of hash state structure.
+ * @param hash Address of location to store the hash.
+ * @param hash_len Number of bytes of @c hash to be stored.
+ *
+ * @return FSL_RETURN_OK_S if all went well, FSL_RETURN_BAD_DATA_LENGTH_S if
+ * hash_len is too long, otherwise an error code.
+ */
+fsl_shw_return_t shw_hash_final(shw_hash_state_t * state,
+ uint8_t * hash, unsigned int hash_len);
+
+#endif /* SHW_HASH_H */
diff --git a/drivers/mxc/security/rng/include/shw_hmac.h b/drivers/mxc/security/rng/include/shw_hmac.h
new file mode 100644
index 000000000000..f73a78f5b02d
--- /dev/null
+++ b/drivers/mxc/security/rng/include/shw_hmac.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+/*!
+ * @file shw_hmac.h
+ *
+ * This file contains definitions for use of the (internal) SHW HMAC
+ * software computation. It defines the usual three steps:
+ *
+ * - #shw_hmac_init()
+ * - #shw_hmac_update()
+ * - #shw_hmac_final()
+ *
+ * The only other item of note to callers is #SHW_HASH_LEN, which is the number
+ * of bytes calculated for the HMAC.
+ */
+
+#ifndef SHW_HMAC_H
+#define SHW_HMAC_H
+
+#include "shw_hash.h"
+
+/*!
+ * State for an HMAC
+ *
+ * Note to callers: This structure contains key material and should be kept in
+ * a secure location, such as internal RAM.
+ */
+typedef struct shw_hmac_state {
+ shw_hash_state_t inner_hash; /*!< Current state of inner hash */
+ shw_hash_state_t outer_hash; /*!< Current state of outer hash */
+} shw_hmac_state_t;
+
+/*!
+ * Initialize the HMAC state structure with the HMAC key
+ *
+ * @param state Address of HMAC state structure.
+ * @param key Address of the key to be used for the HMAC.
+ * @param key_len Number of bytes of @c key. This must not be greater than
+ * the block size of the underlying hash (#SHW_HASH_BLOCK_LEN).
+ *
+ * @return FSL_RETURN_OK_S if all went well, otherwise an error code.
+ */
+fsl_shw_return_t shw_hmac_init(shw_hmac_state_t * state,
+ const uint8_t * key, unsigned int key_len);
+
+/*!
+ * Put data into the HMAC calculation
+ *
+ * @param state Address of HMAC state structure.
+ * @param msg Address of the message data for the HMAC.
+ * @param msg_len Number of bytes of @c msg.
+ *
+ * @return FSL_RETURN_OK_S if all went well, otherwise an error code.
+ */
+fsl_shw_return_t shw_hmac_update(shw_hmac_state_t * state,
+ const uint8_t * msg, unsigned int msg_len);
+
+/*!
+ * Calculate the final HMAC
+ *
+ * @param state Address of HMAC state structure.
+ * @param hmac Address of location to store the HMAC.
+ * @param hmac_len Number of bytes of @c mac to be stored. Probably best if
+ * this value is no greater than #SHW_HASH_LEN.
+ *
+ * @return FSL_RETURN_OK_S if all went well, otherwise an error code.
+ */
+fsl_shw_return_t shw_hmac_final(shw_hmac_state_t * state,
+ uint8_t * hmac, unsigned int hmac_len);
+
+#endif /* SHW_HMAC_H */
diff --git a/drivers/mxc/security/rng/include/shw_internals.h b/drivers/mxc/security/rng/include/shw_internals.h
new file mode 100644
index 000000000000..1bf3b1a86374
--- /dev/null
+++ b/drivers/mxc/security/rng/include/shw_internals.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef SHW_INTERNALS_H
+#define SHW_INTERNALS_H
+
+/*! @file shw_internals.h
+ *
+ * This file contains definitions which are internal to the SHW driver.
+ *
+ * This header file should only ever be included by shw_driver.c
+ *
+ * Compile-time flags minimally needed:
+ *
+ * @li Some sort of platform flag.
+ *
+ */
+
+#include "portable_os.h"
+#include "shw_driver.h"
+
+/*! @defgroup shwcompileflags SHW Compile Flags
+ *
+ * These are flags which are used to configure the SHW driver at compilation
+ * time.
+ *
+ * The terms 'defined' and 'undefined' refer to whether a @c \#define (or -D on
+ * a compile command) has defined a given preprocessor symbol. If a given
+ * symbol is defined, then @c \#ifdef \<symbol\> will succeed. Some symbols
+ * described below default to not having a definition, i.e. they are undefined.
+ *
+ */
+
+/*! @addtogroup shwcompileflags */
+/*! @{ */
+#ifndef SHW_MAJOR_NODE
+/*!
+ * This should be configured in a Makefile/compile command line. It is the
+ * value the driver will use to register itself as a device driver for a
+ * /dev/node file. Zero means allow (Linux) to assign a value. Any positive
+ * number will be attempted as the registration value, to allow for
+ * coordination with the creation/existence of a /dev/fsl_shw (for instance)
+ * file in the filesystem.
+ */
+#define SHW_MAJOR_NODE 0
+#endif
+
+/* Temporarily define compile-time flags to make Doxygen happy and allow them
+ to get into the documentation. */
+#ifdef DOXYGEN_HACK
+
+/*!
+ * Turn on compilation of run-time operational, debug, and error messages.
+ *
+ * This flag is undefined by default.
+ */
+/* REQ-FSLSHW-DEBUG-001 */
+#define SHW_DEBUG
+#undef SHW_DEBUG
+
+/*! @} */
+#endif /* end DOXYGEN_HACK */
+
+#ifndef SHW_DRIVER_NAME
+/*! @addtogroup shwcompileflags */
+/*! @{ */
+/*! Name the driver will use to register itself to the kernel as the driver for
+ * the #shw_major_node and interrupt handling. */
+#define SHW_DRIVER_NAME "fsl_shw"
+/*! @} */
+#endif
+/*#define SHW_DEBUG*/
+
+/*!
+ * Add a user context onto the list of registered users.
+ *
+ * Place it at the head of the #user_list queue.
+ *
+ * @param ctx A pointer to a user context
+ *
+ * @return void
+ */
+inline static void SHW_ADD_USER(fsl_shw_uco_t * ctx)
+{
+ os_lock_context_t lock_context;
+
+ os_lock_save_context(shw_queue_lock, lock_context);
+ ctx->next = user_list;
+ user_list = ctx;
+ os_unlock_restore_context(shw_queue_lock, lock_context);
+
+}
+
+/*!
+ * Remove a user context from the list of registered users.
+ *
+ * @param ctx A pointer to a user context
+ *
+ * @return void
+ *
+ */
+inline static void SHW_REMOVE_USER(fsl_shw_uco_t * ctx)
+{
+ fsl_shw_uco_t *prev_ctx = user_list;
+ os_lock_context_t lock_context;
+
+ os_lock_save_context(shw_queue_lock, lock_context);
+
+ if (prev_ctx == ctx) {
+ /* Found at head, so just set new head */
+ user_list = ctx->next;
+ } else {
+ for (; (prev_ctx != NULL); prev_ctx = prev_ctx->next) {
+ if (prev_ctx->next == ctx) {
+ prev_ctx->next = ctx->next;
+ break;
+ }
+ }
+ }
+ os_unlock_restore_context(shw_queue_lock, lock_context);
+}
+
+static void shw_user_callback(fsl_shw_uco_t * uco);
+
+/* internal functions */
+static os_error_code shw_setup_user_driver_interaction(void);
+static void shw_cleanup(void);
+
+static os_error_code init_uco(fsl_shw_uco_t * user_ctx, void *user_mode_uco);
+static os_error_code get_capabilities(fsl_shw_uco_t * user_ctx,
+ void *user_mode_pco_request);
+static os_error_code get_results(fsl_shw_uco_t * user_ctx,
+ void *user_mode_result_req);
+static os_error_code get_random(fsl_shw_uco_t * user_ctx,
+ void *user_mode_get_random_req);
+static os_error_code add_entropy(fsl_shw_uco_t * user_ctx,
+ void *user_mode_add_entropy_req);
+
+void* wire_user_memory(void* address, uint32_t length, void** page_ctx);
+void unwire_user_memory(void** page_ctx);
+os_error_code map_user_memory(struct vm_area_struct* vma,
+ uint32_t physical_addr, uint32_t size);
+os_error_code unmap_user_memory(uint32_t user_addr, uint32_t size);
+
+#if defined(LINUX_VERSION_CODE)
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("Device Driver for FSL SHW API");
+
+#endif /* LINUX_VERSION_CODE */
+
+#endif /* SHW_INTERNALS_H */
diff --git a/drivers/mxc/security/rng/rng_driver.c b/drivers/mxc/security/rng/rng_driver.c
new file mode 100644
index 000000000000..51eda5ccbe61
--- /dev/null
+++ b/drivers/mxc/security/rng/rng_driver.c
@@ -0,0 +1,1150 @@
+/*
+ * Copyright (C) 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*! @file rng_driver.c
+ *
+ * This is the driver code for the hardware Random Number Generator (RNG).
+ *
+ * It provides the following functions to callers:
+ * fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t* user_ctx,
+ * uint32_t length,
+ * uint8_t* data);
+ *
+ * fsl_shw_return_t fsl_shw_add_entropy(fsl_shw_uco_t* user_ctx,
+ * uint32_t length,
+ * uint8_t* data);
+ *
+ * The life of the driver starts at boot (or module load) time, with a call by
+ * the kernel to #rng_init(). As part of initialization, a background task
+ * running #rng_entropy_task() will be created.
+ *
+ * The life of the driver ends when the kernel is shutting down (or the driver
+ * is being unloaded). At this time, #rng_shutdown() is called. No function
+ * will ever be called after that point. In the case that the driver is
+ * reloaded, a new copy of the driver, with fresh global values, etc., is
+ * loaded, and there will be a new call to #rng_init().
+ *
+ * A call to fsl_shw_get_random() gets converted into a work entry which is
+ * queued and handed off to a background task for fulfilment. This provides
+ * for a single thread of control for reading the RNG's FIFO register, which
+ * might otherwise underflow if not carefully managed.
+ *
+ * A call to fsl_shw_add_entropy() will cause the additional entropy to
+ * be passed directly into the hardware.
+ *
+ * In a debug configuration, it provides the following kernel functions:
+ * rng_return_t rng_read_register(uint32_t byte_offset, uint32_t* valuep);
+ * rng_return_t rng_write_register(uint32_t byte_offset, uint32_t value);
+ * @ingroup RNG
+ */
+
+#include "portable_os.h"
+#include "fsl_shw.h"
+#include "rng_internals.h"
+
+#ifdef FSL_HAVE_SCC2
+#include <linux/mxc_scc2_driver.h>
+#else
+#include <linux/mxc_scc_driver.h>
+#endif
+
+#if defined(RNG_DEBUG) || defined(RNG_ENTROPY_DEBUG) || \
+ defined(RNG_REGISTER_DEBUG)
+
+#include <diagnostic.h>
+
+#else
+
+#define LOG_KDIAG_ARGS(fmt, ...)
+#define LOG_KDIAG(diag)
+
+#endif
+
+/* These are often handy */
+#ifndef FALSE
+/*! Non-true value for arguments, return values. */
+#define FALSE 0
+#endif
+#ifndef TRUE
+/*! True value for arguments, return values. */
+#define TRUE 1
+#endif
+
+/******************************************************************************
+ *
+ * Global / Static Variables
+ *
+ *****************************************************************************/
+
+/*!
+ * This is type void* so that a) it cannot directly be dereferenced, and b)
+ * pointer arithmetic on it will function for the byte offsets in rng_rnga.h
+ * and rng_rngc.h
+ *
+ * rng_base is the location in the iomap where the RNG's registers
+ * (and memory) start.
+ *
+ * The referenced data is declared volatile so that the compiler will
+ * not make any assumptions about the value of registers in the RNG,
+ * and thus will always reload the register into CPU memory before
+ * using it (i.e. wherever it is referenced in the driver).
+ *
+ * This value should only be referenced by the #RNG_READ_REGISTER and
+ * #RNG_WRITE_REGISTER macros and their ilk. All dereferences must be
+ * 32 bits wide.
+ */
+static volatile void *rng_base;
+
+/*!
+ * Flag to say whether interrupt handler has been registered for RNG
+ * interrupt */
+static int rng_irq_set = FALSE;
+
+/*!
+ * Size of the RNG's OUTPUT_FIFO, in words. Retrieved with
+ * #RNG_GET_FIFO_SIZE() during driver initialization.
+ */
+static int rng_output_fifo_size;
+
+/*! Major number for device driver. */
+static int rng_major;
+
+/*! Registration handle for registering driver with OS. */
+os_driver_reg_t rng_reg_handle;
+
+/*!
+ * Internal flag to know whether RNG is in Failed state (and thus many
+ * registers are unavailable). If the value ever goes to #RNG_STATUS_FAILED,
+ * it will never change.
+ */
+static volatile rng_status_t rng_availability = RNG_STATUS_INITIAL;
+
+/*!
+ * Global lock for the RNG driver. Mainly used for entries on the RNG work
+ * queue.
+ */
+static os_lock_t rng_queue_lock = NULL;
+
+/*!
+ * Queue for the RNG task to process.
+ */
+static shw_queue_t rng_work_queue;
+
+/*!
+ * Flag to say whether task initialization succeeded.
+ */
+static unsigned task_started = FALSE;
+/*!
+ * Waiting queue for RNG SELF TESTING
+ */
+static DECLARE_COMPLETION(rng_self_testing);
+static DECLARE_COMPLETION(rng_seed_done);
+/*!
+ * Object for blocking-mode callers of RNG driver to sleep.
+ */
+OS_WAIT_OBJECT(rng_wait_queue);
+
+/******************************************************************************
+ *
+ * Function Implementations - Externally Accessible
+ *
+ *****************************************************************************/
+
+/*****************************************************************************/
+/* fn rng_init() */
+/*****************************************************************************/
+/*!
+ * Initialize the driver.
+ *
+ * Set up the driver to have access to RNG device registers and verify that
+ * it appears to be a proper working device.
+ *
+ * Set up interrupt handling. Assure RNG is ready to go and (possibly) set it
+ * into High Assurance mode. Create a background task to run
+ * #rng_entropy_task(). Set up up a callback with the SCC driver should the
+ * security alarm go off. Tell the kernel that the driver is here.
+ *
+ * This routine is called during kernel init or module load (insmod).
+ *
+ * The function will fail in one of two ways: Returning OK to the caller so
+ * that kernel startup / driver initialization completes, or returning an
+ * error. In the success case, the function could set the rng_avaailability to
+ * RNG_STATUS_FAILED so that only minimal support (e.g. register peek / poke)
+ * is available in the driver.
+ *
+ * @return a call to os_dev_init_return()
+ */
+OS_DEV_INIT(rng_init)
+{
+ struct clk *clk;
+ os_error_code return_code = OS_ERROR_FAIL_S;
+ rng_availability = RNG_STATUS_CHECKING;
+
+#if !defined(FSL_HAVE_RNGA)
+ INIT_COMPLETION(rng_self_testing);
+ INIT_COMPLETION(rng_seed_done);
+#endif
+ rng_work_queue.head = NULL;
+ rng_work_queue.tail = NULL;
+
+ clk = clk_get(NULL, "rng_clk");
+
+ // Check that the clock was found
+ if (IS_ERR(clk)) {
+ LOG_KDIAG("RNG: Failed to find rng_clock.");
+ return_code = OS_ERROR_FAIL_S;
+ goto check_err;
+ }
+
+ clk_enable(clk);
+
+ os_printk(KERN_INFO "RNG Driver: Loading\n");
+
+ return_code = rng_map_RNG_memory();
+ if (return_code != OS_ERROR_OK_S) {
+ rng_availability = RNG_STATUS_UNIMPLEMENTED;
+ LOG_KDIAG_ARGS("RNG: Driver failed to map RNG registers. %d",
+ return_code);
+ goto check_err;
+ }
+ LOG_KDIAG_ARGS("RNG Driver: rng_base is 0x%08x", (uint32_t) rng_base);
+ /*Check SCC keys are fused */
+ if (RNG_HAS_ERROR()) {
+ if (RNG_HAS_BAD_KEY()) {
+#ifdef RNG_DEBUG
+#if !defined(FSL_HAVE_RNGA)
+ LOG_KDIAG("ERROR: BAD KEYS SELECTED");
+ {
+ uint32_t rngc_status =
+ RNG_READ_REGISTER(RNGC_STATUS);
+ uint32_t rngc_error =
+ RNG_READ_REGISTER(RNGC_ERROR);
+ LOG_KDIAG_ARGS
+ ("status register: %08x, error status: %08x",
+ rngc_status, rngc_error);
+ }
+#endif
+#endif
+ rng_availability = RNG_STATUS_FAILED;
+ return_code = OS_ERROR_FAIL_S;
+ goto check_err;
+ }
+ }
+
+ /* Check RNG configuration and status */
+ return_code = rng_grab_config_values();
+ if (return_code != OS_ERROR_OK_S) {
+ rng_availability = RNG_STATUS_UNIMPLEMENTED;
+ goto check_err;
+ }
+
+ /* Masking All Interrupts */
+ /* They are unmasked later in rng_setup_interrupt_handling() */
+ RNG_MASK_ALL_INTERRUPTS();
+
+ RNG_WAKE();
+
+ /* Determine status of RNG */
+ if (RNG_OSCILLATOR_FAILED()) {
+ LOG_KDIAG("RNG Driver: RNG Oscillator is dead");
+ rng_availability = RNG_STATUS_FAILED;
+ goto check_err;
+ }
+
+ /* Oscillator not dead. Setup interrupt code and start the RNG. */
+ if ((return_code = rng_setup_interrupt_handling()) == OS_ERROR_OK_S) {
+#if defined(FSL_HAVE_RNGA)
+ scc_return_t scc_code;
+#endif
+
+ RNG_GO();
+
+ /* Self Testing For RNG */
+ do {
+ RNG_CLEAR_ERR();
+
+ /* wait for Clearing Erring finished */
+ msleep(1);
+
+ RNG_UNMASK_ALL_INTERRUPTS();
+ RNG_SELF_TEST();
+#if !defined(FSL_HAVE_RNGA)
+ wait_for_completion(&rng_self_testing);
+#endif
+ } while (RNG_CHECK_SELF_ERR());
+
+ RNG_CLEAR_ALL_STATUS();
+ /* checking for RNG SEED done */
+ do {
+ RNG_CLEAR_ERR();
+ RNG_SEED_GEN();
+#if !defined(FSL_HAVE_RNGA)
+ wait_for_completion(&rng_seed_done);
+#endif
+ } while (RNG_CHECK_SEED_ERR());
+#ifndef RNG_NO_FORCE_HIGH_ASSURANCE
+ RNG_SET_HIGH_ASSURANCE();
+#endif
+ if (RNG_GET_HIGH_ASSURANCE()) {
+ LOG_KDIAG("RNG Driver: RNG is in High Assurance mode");
+ } else {
+#ifndef RNG_NO_FORCE_HIGH_ASSURANCE
+ LOG_KDIAG
+ ("RNG Driver: RNG could not be put in High Assurance mode");
+ rng_availability = RNG_STATUS_FAILED;
+ goto check_err;
+#endif /* RNG_NO_FORCE_HIGH_ASSURANCE */
+ }
+
+ /* Check that RNG is OK */
+ if (!RNG_WORKING()) {
+ LOG_KDIAG_ARGS
+ ("RNG determined to be inoperable. Status %08x",
+ RNG_GET_STATUS());
+ /* Couldn't wake it up or other problem */
+ rng_availability = RNG_STATUS_FAILED;
+ goto check_err;
+ }
+
+ rng_queue_lock = os_lock_alloc_init();
+ if (rng_queue_lock == NULL) {
+ LOG_KDIAG("RNG: lock initialization failed");
+ rng_availability = RNG_STATUS_FAILED;
+ goto check_err;
+ }
+
+ return_code = os_create_task(rng_entropy_task);
+ if (return_code != OS_ERROR_OK_S) {
+ LOG_KDIAG("RNG: task initialization failed");
+ rng_availability = RNG_STATUS_FAILED;
+ goto check_err;
+ } else {
+ task_started = TRUE;
+ }
+#ifdef FSL_HAVE_RNGA
+ scc_code = scc_monitor_security_failure(rng_sec_failure);
+ if (scc_code != SCC_RET_OK) {
+ LOG_KDIAG_ARGS("Failed to register SCC callback: %d",
+ scc_code);
+#ifndef RNG_NO_FORCE_HIGH_ASSURANCE
+ return_code = OS_ERROR_FAIL_S;
+ goto check_err;
+#endif
+ }
+#endif /* FSL_HAVE_RNGA */
+ return_code = os_driver_init_registration(rng_reg_handle);
+ if (return_code != OS_ERROR_OK_S) {
+ goto check_err;
+ }
+ /* add power suspend here */
+ /* add power resume here */
+ return_code =
+ os_driver_complete_registration(rng_reg_handle,
+ rng_major, RNG_DRIVER_NAME);
+ }
+ /* RNG is working */
+
+ check_err:
+
+ /* If FIFO underflow or other error occurred during drain, this will fail,
+ * as system will have been put into fail mode by SCC. */
+ if ((return_code == OS_ERROR_OK_S)
+ && (rng_availability == RNG_STATUS_CHECKING)) {
+ RNG_PUT_RNG_TO_SLEEP();
+ rng_availability = RNG_STATUS_OK; /* RNG & driver are ready */
+ } else if (return_code != OS_ERROR_OK_S) {
+ os_printk(KERN_ALERT "Driver initialization failed. %d",
+ return_code);
+ rng_cleanup();
+ }
+
+ os_dev_init_return(return_code);
+
+} /* rng_init */
+
+/*****************************************************************************/
+/* fn rng_shutdown() */
+/*****************************************************************************/
+/*!
+ * Prepare driver for exit.
+ *
+ * This is called during @c rmmod when the driver is unloading.
+ * Try to undo whatever was done during #rng_init(), to make the machine be
+ * in the same state, if possible.
+ *
+ * Calls rng_cleanup() to do all work, and then unmap device's register space.
+ */
+OS_DEV_SHUTDOWN(rng_shutdown)
+{
+ LOG_KDIAG("shutdown called");
+
+ rng_cleanup();
+
+ os_driver_remove_registration(rng_reg_handle);
+ if (rng_base != NULL) {
+ /* release the virtual memory map to the RNG */
+ os_unmap_device((void *)rng_base, RNG_ADDRESS_RANGE);
+ rng_base = NULL;
+ }
+
+ os_dev_shutdown_return(OS_ERROR_OK_S);
+} /* rng_shutdown */
+
+/*****************************************************************************/
+/* fn rng_cleanup() */
+/*****************************************************************************/
+/*!
+ * Undo everything done by rng_init() and place driver in fail mode.
+ *
+ * Deregister from SCC, stop tasklet, shutdown the RNG. Leave the register
+ * map in place in case other drivers call rng_read/write_register()
+ *
+ * @return void
+ */
+static void rng_cleanup(void)
+{
+ struct clk *clk;
+
+#ifdef FSL_HAVE_RNGA
+ scc_stop_monitoring_security_failure(rng_sec_failure);
+#endif
+
+ clk = clk_get(NULL, "rng_clk");
+ clk_disable(clk);
+ if (task_started) {
+ os_dev_stop_task(rng_entropy_task);
+ }
+
+ if (rng_base != NULL) {
+ /* mask off RNG interrupts */
+ RNG_MASK_ALL_INTERRUPTS();
+ RNG_SLEEP();
+
+ if (rng_irq_set) {
+ /* unmap the interrupts from the IRQ lines */
+ os_deregister_interrupt(INT_RNG);
+ rng_irq_set = FALSE;
+ }
+ LOG_KDIAG("Leaving rng driver status as failed");
+ rng_availability = RNG_STATUS_FAILED;
+ } else {
+ LOG_KDIAG("Leaving rng driver status as unimplemented");
+ rng_availability = RNG_STATUS_UNIMPLEMENTED;
+ }
+ LOG_KDIAG("Cleaned up");
+} /* rng_cleanup */
+
+/*!
+ * Post-process routine for fsl_shw_get_random().
+ *
+ * This function will copy the random data generated by the background task
+ * into the user's buffer and then free the local buffer.
+ *
+ * @param gen_entry The work request.
+ *
+ * @return 0 = meaning work completed, pass back result.
+ */
+static uint32_t finish_random(shw_queue_entry_t * gen_entry)
+{
+ rng_work_entry_t *work = (rng_work_entry_t *) gen_entry;
+
+ if (work->hdr.flags & FSL_UCO_USERMODE_USER) {
+ os_copy_to_user(work->data_user, work->data_local,
+ work->length);
+ } else {
+ memcpy(work->data_user, work->data_local, work->length);
+ }
+
+ os_free_memory(work->data_local);
+ work->data_local = NULL;
+
+ return 0; /* means completed. */
+}
+
+/* REQ-FSLSHW-PINTFC-API-BASIC-RNG-002 */
+/*****************************************************************************/
+/* fn fsl_shw_get_random() */
+/*****************************************************************************/
+/*!
+ * Get random data.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param length The number of octets of @a data being requested.
+ * @param data A pointer to a location of @a length octets to where
+ * random data will be returned.
+ *
+ * @return FSL_RETURN_NO_RESOURCE_S A return code of type #fsl_shw_return_t.
+ * FSL_RETURN_OK_S
+ */
+fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t * user_ctx, uint32_t length,
+ uint8_t * data)
+{
+ fsl_shw_return_t return_code = FSL_RETURN_NO_RESOURCE_S;
+ /* Boost up length to cover any 'missing' bytes at end of a word */
+ uint32_t *buf = os_alloc_memory(length + 3, 0);
+ volatile rng_work_entry_t *work = os_alloc_memory(sizeof(*work), 0);
+
+ if ((rng_availability != RNG_STATUS_OK) || (buf == NULL)
+ || (work == NULL)) {
+ if (rng_availability != RNG_STATUS_OK) {
+ LOG_KDIAG_ARGS("rng not available: %d\n",
+ rng_availability);
+ } else {
+ LOG_KDIAG_ARGS
+ ("Resource allocation failure: %d or %d bytes",
+ length, sizeof(*work));
+ }
+ /* Cannot perform function. Clean up and clear out. */
+ if (buf != NULL) {
+ os_free_memory(buf);
+ }
+ if (work != NULL) {
+ os_free_memory((void *)work);
+ }
+ } else {
+ unsigned blocking = user_ctx->flags & FSL_UCO_BLOCKING_MODE;
+
+ work->hdr.user_ctx = user_ctx;
+ work->hdr.flags = user_ctx->flags;
+ work->hdr.callback = user_ctx->callback;
+ work->hdr.user_ref = user_ctx->user_ref;
+ work->hdr.postprocess = finish_random;
+ work->length = length;
+ work->data_local = buf;
+ work->data_user = data;
+
+ RNG_ADD_WORK_ENTRY((rng_work_entry_t *) work);
+
+ if (blocking) {
+ os_sleep(rng_wait_queue, work->completed != FALSE,
+ FALSE);
+ finish_random((shw_queue_entry_t *) work);
+ return_code = work->hdr.code;
+ os_free_memory((void *)work);
+ } else {
+ return_code = FSL_RETURN_OK_S;
+ }
+ }
+
+ return return_code;
+} /* fsl_shw_get_entropy */
+
+/*****************************************************************************/
+/* fn fsl_shw_add_entropy() */
+/*****************************************************************************/
+/*!
+ * Add entropy to random number generator.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param length Number of bytes at @a data.
+ * @param data Entropy to add to random number generator.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_add_entropy(fsl_shw_uco_t * user_ctx, uint32_t length,
+ uint8_t * data)
+{
+ fsl_shw_return_t return_code = FSL_RETURN_NO_RESOURCE_S;
+#if defined(FSL_HAVE_RNGC)
+ /* No Entropy Register in RNGC */
+ return_code = FSL_RETURN_OK_S;
+#else
+ uint32_t *local_data = NULL;
+ if (rng_availability == RNG_STATUS_OK) {
+ /* make 32-bit aligned place to hold data */
+ local_data = os_alloc_memory(length + 3, 0);
+ if (local_data == NULL) {
+ return_code = FSL_RETURN_NO_RESOURCE_S;
+ } else {
+ memcpy(local_data, data, length);
+
+ /* Copy one word at a time to hardware */
+ while (TRUE) {
+ register uint32_t *ptr = local_data;
+
+ RNG_ADD_ENTROPY(*ptr++);
+ if (length <= 4) {
+ break;
+ }
+ length -= 4;
+ }
+ return_code = FSL_RETURN_OK_S;
+ os_free_memory(local_data);
+ } /* else local_data not NULL */
+
+ }
+#endif
+ /* rng_availability is OK */
+ return return_code;
+} /* fsl_shw_add_entropy */
+
+#ifdef RNG_REGISTER_PEEK_POKE
+/*****************************************************************************/
+/* fn rng_read_register() */
+/*****************************************************************************/
+/*
+ * Debug routines to allow reading of RNG registers.
+ *
+ * This routine is only for accesses by other than this driver.
+ *
+ * @param register_offset The byte offset of the register to be read.
+ * @param value Pointer to store the value of the register.
+ *
+ * @return RNG_RET_OK or an error return.
+ */
+rng_return_t rng_read_register(uint32_t register_offset, uint32_t * value)
+{
+ rng_return_t return_code = RNG_RET_FAIL;
+
+ if ((rng_availability == RNG_STATUS_OK)
+ || (rng_availability == RNG_STATUS_FAILED)) {
+ if ((rng_check_register_offset(register_offset)
+ && rng_check_register_accessible(register_offset,
+ RNG_CHECK_READ))) {
+ /* The guards let the check through */
+ *value = RNG_READ_REGISTER(register_offset);
+ return_code = RNG_RET_OK;
+ }
+ }
+
+ return return_code;
+} /* rng_read_register */
+
+/*****************************************************************************/
+/* fn rng_write_register() */
+/*****************************************************************************/
+/*
+ * Debug routines to allow writing of RNG registers.
+ *
+ * This routine is only for accesses by other than this driver.
+ *
+ * @param register_offset The byte offset of the register to be written.
+ * @param value Value to store in the register.
+ *
+ * @return RNG_RET_OK or an error return.
+ */
+rng_return_t rng_write_register(uint32_t register_offset, uint32_t value)
+{
+ rng_return_t return_code = RNG_RET_FAIL;
+
+ if ((rng_availability == RNG_STATUS_OK)
+ || (rng_availability == RNG_STATUS_FAILED)) {
+ if ((rng_check_register_offset(register_offset)
+ && rng_check_register_accessible(register_offset,
+ RNG_CHECK_WRITE))) {
+ RNG_WRITE_REGISTER(register_offset, value);
+ return_code = RNG_RET_OK;
+ }
+ }
+
+ return return_code;
+} /* rng_write_register */
+#endif /* RNG_REGISTER_PEEK_POKE */
+
+/******************************************************************************
+ *
+ * Function Implementations - Internal
+ *
+ *****************************************************************************/
+
+#ifdef RNG_REGISTER_PEEK_POKE
+/*****************************************************************************/
+/* fn check_register_offset() */
+/*****************************************************************************/
+/*!
+ * Verify that the @c offset is appropriate for the RNG's register set.
+ *
+ * @param[in] offset The (byte) offset within the RNG block
+ * of the register to be accessed. See
+ * RNG(A, C) register definitions for meanings.
+ *
+ * This routine is only for checking accesses by other than this driver.
+ *
+ * @return 0 if register offset out of bounds, 1 if ok to use
+ */
+inline int rng_check_register_offset(uint32_t offset)
+{
+ int return_code = FALSE; /* invalid */
+
+ /* Make sure offset isn't too high and also that it is aligned to
+ * aa 32-bit offset (multiple of four).
+ */
+ if ((offset < RNG_ADDRESS_RANGE) && (offset % sizeof(uint32_t) == 0)) {
+ return_code = TRUE; /* OK */
+ } else {
+ pr_debug("RNG: Denied access to offset %8x\n", offset);
+ }
+
+ return return_code;
+
+} /* rng_check_register */
+
+/*****************************************************************************/
+/* fn check_register_accessible() */
+/*****************************************************************************/
+/*!
+ * Make sure that register access is legal.
+ *
+ * Verify that, if in secure mode, only safe registers are used.
+ * For any register access, make sure that read-only registers are not written
+ * and that write-only registers are not read. This check also disallows any
+ * access to the RNG's Output FIFO, to prevent other drivers from draining the
+ * FIFO and causing an underflow condition.
+ *
+ * This routine is only for checking accesses by other than this driver.
+ *
+ * @param offset The (byte) offset within the RNG block
+ * of the register to be accessed. See
+ * @ref rngregs for meanings.
+ * @param access_write 0 for read, anything else for write
+ *
+ * @return 0 if invalid, 1 if OK.
+ */
+static int rng_check_register_accessible(uint32_t offset, int access_write)
+{
+ int return_code = FALSE; /* invalid */
+ uint32_t secure = RNG_GET_HIGH_ASSURANCE();
+
+ /* First check for RNG in Secure Mode -- most registers inaccessible.
+ * Also disallowing access to RNG_OUTPUT_FIFO except by the driver.
+ */
+ if (!
+#ifdef FSL_HAVE_RNGA
+ (secure &&
+ ((offset == RNGA_OUTPUT_FIFO) ||
+ (offset == RNGA_MODE) ||
+ (offset == RNGA_VERIFICATION_CONTROL) ||
+ (offset == RNGA_OSCILLATOR_CONTROL_COUNTER) ||
+ (offset == RNGA_OSCILLATOR1_COUNTER) ||
+ (offset == RNGA_OSCILLATOR2_COUNTER) ||
+ (offset == RNGA_OSCILLATOR_COUNTER_STATUS)))
+#else /* RNGB or RNGC */
+ (secure &&
+ ((offset == RNGC_FIFO) ||
+ (offset == RNGC_VERIFICATION_CONTROL) ||
+ (offset == RNGC_OSC_COUNTER_CONTROL) ||
+ (offset == RNGC_OSC_COUNTER) ||
+ (offset == RNGC_OSC_COUNTER_STATUS)))
+#endif
+ ) {
+
+ /* Passed that test. Either not in high assurance, and/or are
+ checking register that is always available. Now check
+ R/W permissions. */
+ if (access_write == RNG_CHECK_READ) { /* read request */
+ /* Only the entropy register is write-only */
+#ifdef FSL_HAVE_RNGC
+ /* No registers are write-only */
+ return_code = TRUE;
+#else /* else RNGA or RNGB */
+#ifdef FSL_HAVE_RNGA
+ if (1) {
+#else
+ if (!(offset == RNGB_ENTROPY)) {
+#endif
+ return_code = TRUE; /* Let all others be read */
+ } else {
+ pr_debug
+ ("RNG: Offset %04x denied read access\n",
+ offset);
+ }
+#endif /* RNGA or RNGB */
+ } /* read */
+ else { /* access_write means write */
+ /* Check against list of non-writable registers */
+ if (!
+#ifdef FSL_HAVE_RNGA
+ ((offset == RNGA_STATUS) ||
+ (offset == RNGA_OUTPUT_FIFO) ||
+ (offset == RNGA_OSCILLATOR1_COUNTER) ||
+ (offset == RNGA_OSCILLATOR2_COUNTER) ||
+ (offset == RNGA_OSCILLATOR_COUNTER_STATUS))
+#else /* FSL_HAVE_RNGB or FSL_HAVE_RNGC */
+ ((offset == RNGC_STATUS) ||
+ (offset == RNGC_FIFO) ||
+ (offset == RNGC_OSC_COUNTER) ||
+ (offset == RNGC_OSC_COUNTER_STATUS))
+#endif
+ ) {
+ return_code = TRUE; /* can be written */
+ } else {
+ LOG_KDIAG_ARGS
+ ("Offset %04x denied write access", offset);
+ }
+ } /* write */
+ } /* not high assurance and inaccessible register... */
+ else {
+ LOG_KDIAG_ARGS("Offset %04x denied high-assurance access",
+ offset);
+ }
+
+ return return_code;
+} /* rng_check_register_accessible */
+#endif /* RNG_REGISTER_PEEK_POKE */
+
+/*****************************************************************************/
+/* fn rng_irq() */
+/*****************************************************************************/
+/*!
+ * This is the interrupt handler for the RNG. It is only ever invoked if the
+ * RNG detects a FIFO Underflow error.
+ *
+ * If the error is a Security Violation, this routine will
+ * set the #rng_availability to #RNG_STATUS_FAILED, as the entropy pool may
+ * have been corrupted. The RNG will also be placed into low power mode. The
+ * SCC will have noticed the problem as well.
+ *
+ * The other possibility, if the RNG is not in High Assurance mode, would be
+ * simply a FIFO Underflow. No special action, other than to
+ * clear the interrupt, is taken.
+ */
+OS_DEV_ISR(rng_irq)
+{
+ int handled = FALSE; /* assume interrupt isn't from RNG */
+
+ LOG_KDIAG("rng irq!");
+
+ if (RNG_SEED_DONE()) {
+ complete(&rng_seed_done);
+ RNG_CLEAR_ALL_STATUS();
+ handled = TRUE;
+ }
+
+ if (RNG_SELF_TEST_DONE()) {
+ complete(&rng_self_testing);
+ RNG_CLEAR_ALL_STATUS();
+ handled = TRUE;
+ }
+ /* Look to see whether RNG needs attention */
+ if (RNG_HAS_ERROR()) {
+ if (RNG_GET_HIGH_ASSURANCE()) {
+ RNG_SLEEP();
+ rng_availability = RNG_STATUS_FAILED;
+ RNG_MASK_ALL_INTERRUPTS();
+ }
+ handled = TRUE;
+ /* Clear the interrupt */
+ RNG_CLEAR_ALL_STATUS();
+
+ }
+ os_dev_isr_return(handled);
+} /* rng_irq */
+
+/*****************************************************************************/
+/* fn map_RNG_memory() */
+/*****************************************************************************/
+/*!
+ * Place the RNG's memory into kernel virtual space.
+ *
+ * @return OS_ERROR_OK_S on success, os_error_code on failure
+ */
+static os_error_code rng_map_RNG_memory(void)
+{
+ os_error_code error_code = OS_ERROR_FAIL_S;
+
+ rng_base = os_map_device(RNG_BASE_ADDR, RNG_ADDRESS_RANGE);
+ if (rng_base == NULL) {
+ /* failure ! */
+ LOG_KDIAG("RNG Driver: ioremap failed.");
+ } else {
+ error_code = OS_ERROR_OK_S;
+ }
+
+ return error_code;
+} /* rng_map_RNG_memory */
+
+/*****************************************************************************/
+/* fn rng_setup_interrupt_handling() */
+/*****************************************************************************/
+/*!
+ * Register #rng_irq() as the interrupt handler for #INT_RNG.
+ *
+ * @return OS_ERROR_OK_S on success, os_error_code on failure
+ */
+static os_error_code rng_setup_interrupt_handling(void)
+{
+ os_error_code error_code;
+
+ /*
+ * Install interrupt service routine for the RNG. Ignore the
+ * assigned IRQ number.
+ */
+ error_code = os_register_interrupt(RNG_DRIVER_NAME, INT_RNG,
+ OS_DEV_ISR_REF(rng_irq));
+ if (error_code != OS_ERROR_OK_S) {
+ LOG_KDIAG("RNG Driver: Error installing Interrupt Handler");
+ } else {
+ rng_irq_set = TRUE;
+ RNG_UNMASK_ALL_INTERRUPTS();
+ }
+
+ return error_code;
+} /* rng_setup_interrupt_handling */
+
+/*****************************************************************************/
+/* fn rng_grab_config_values() */
+/*****************************************************************************/
+/*!
+ * Read configuration information from the RNG.
+ *
+ * Sets #rng_output_fifo_size.
+ *
+ * @return A error code indicating whether the part is the expected one.
+ */
+static os_error_code rng_grab_config_values(void)
+{
+ enum rng_type type;
+ os_error_code ret = OS_ERROR_FAIL_S;
+
+ /* Go for type, versions... */
+ type = RNG_GET_RNG_TYPE();
+
+ /* Make sure type is the one this code has been compiled for. */
+ if (RNG_VERIFY_TYPE(type)) {
+ rng_output_fifo_size = RNG_GET_FIFO_SIZE();
+ if (rng_output_fifo_size != 0) {
+ ret = OS_ERROR_OK_S;
+ }
+ }
+ if (ret != OS_ERROR_OK_S) {
+ LOG_KDIAG_ARGS
+ ("Unknown or unexpected RNG type %d (FIFO size %d)."
+ " Failing driver initialization", type,
+ rng_output_fifo_size);
+ }
+
+ return ret;
+}
+
+ /* rng_grab_config_values */
+
+/*****************************************************************************/
+/* fn rng_drain_fifo() */
+/*****************************************************************************/
+/*!
+ * This function copies words from the RNG FIFO into the caller's buffer.
+ *
+ *
+ * @param random_p Location to copy random data
+ * @param count_words Number of words to copy
+ *
+ * @return An error code.
+ */
+static fsl_shw_return_t rng_drain_fifo(uint32_t * random_p, int count_words)
+{
+
+ int words_in_rng; /* Number of words available now in RNG */
+ fsl_shw_return_t code = FSL_RETURN_ERROR_S;
+ int sequential_count = 0; /* times through big while w/empty FIFO */
+ int fifo_empty_count = 0; /* number of times FIFO was empty */
+ int max_sequential = 0; /* max times 0 seen in a row */
+#if !defined(FSL_HAVE_RNGA)
+ int count_for_reseed = 0;
+ INIT_COMPLETION(rng_seed_done);
+#endif
+#if !defined(FSL_HAVE_RNGA)
+ if (RNG_RESEED()) {
+ do {
+ LOG_KDIAG("Reseeding RNG");
+
+ RNG_CLEAR_ERR();
+ RNG_SEED_GEN();
+ wait_for_completion(&rng_seed_done);
+ if (count_for_reseed == 3) {
+ os_printk(KERN_ALERT
+ "Device was not able to enter RESEED Mode\n");
+ code = FSL_RETURN_INTERNAL_ERROR_S;
+ }
+ count_for_reseed++;
+ } while (RNG_CHECK_SEED_ERR());
+ }
+#endif
+ /* Copy all of them in. Stop if pool fills. */
+ while ((rng_availability == RNG_STATUS_OK) && (count_words > 0)) {
+ /* Ask RNG how many words currently in FIFO */
+ words_in_rng = RNG_GET_WORDS_IN_FIFO();
+ if (words_in_rng == 0) {
+ ++sequential_count;
+ fifo_empty_count++;
+ if (sequential_count > max_sequential) {
+ max_sequential = sequential_count;
+ }
+ if (sequential_count >= RNG_MAX_TRIES) {
+ LOG_KDIAG_ARGS("FIFO staying empty (%d)",
+ words_in_rng);
+ code = FSL_RETURN_NO_RESOURCE_S;
+ break;
+ }
+ } else {
+ /* Found at least one word */
+ sequential_count = 0;
+ /* Now adjust: words_in_rng = MAX(count_words, words_in_rng) */
+ words_in_rng = (count_words < words_in_rng)
+ ? count_words : words_in_rng;
+ } /* else found words */
+
+#ifdef RNG_FORCE_FIFO_UNDERFLOW
+ /*
+ * For unit test, force occasional extraction of more words than
+ * available. This should cause FIFO Underflow, and IRQ invocation.
+ */
+ words_in_rng = count_words;
+#endif
+
+ /* Copy out all available & neeeded data */
+ while (words_in_rng-- > 0) {
+ *random_p++ = RNG_READ_FIFO();
+ count_words--;
+ }
+ } /* while words still needed */
+
+ if (count_words == 0) {
+ code = FSL_RETURN_OK_S;
+ }
+ if (fifo_empty_count != 0) {
+ LOG_KDIAG_ARGS("FIFO empty %d times, max loop count %d",
+ fifo_empty_count, max_sequential);
+ }
+
+ return code;
+} /* rng_drain_fifo */
+
+/*****************************************************************************/
+/* fn rng_entropy_task() */
+/*****************************************************************************/
+/*!
+ * This is the background task of the driver. It is scheduled by
+ * RNG_ADD_WORK_ENTRY().
+ *
+ * This will process each entry on the #rng_work_queue. Blocking requests will
+ * cause sleepers to be awoken. Non-blocking requests will be placed on the
+ * results queue, and if appropriate, the callback function will be invoked.
+ */
+OS_DEV_TASK(rng_entropy_task)
+{
+ rng_work_entry_t *work;
+
+ os_dev_task_begin();
+
+#ifdef RNG_ENTROPY_DEBUG
+ LOG_KDIAG("entropy task starting");
+#endif
+
+ while ((work = RNG_GET_WORK_ENTRY()) != NULL) {
+#ifdef RNG_ENTROPY_DEBUG
+ LOG_KDIAG_ARGS("found %d bytes of work at %p (%p)",
+ work->length, work, work->data_local);
+#endif
+ work->hdr.code = rng_drain_fifo(work->data_local,
+ BYTES_TO_WORDS(work->length));
+ work->completed = TRUE;
+
+ if (work->hdr.flags & FSL_UCO_BLOCKING_MODE) {
+#ifdef RNG_ENTROPY_DEBUG
+ LOG_KDIAG("Waking queued processes");
+#endif
+ os_wake_sleepers(rng_wait_queue);
+ } else {
+ os_lock_context_t lock_context;
+
+ os_lock_save_context(rng_queue_lock, lock_context);
+ RNG_ADD_QUEUE_ENTRY(&work->hdr.user_ctx->result_pool,
+ work);
+ os_unlock_restore_context(rng_queue_lock, lock_context);
+
+ if (work->hdr.flags & FSL_UCO_CALLBACK_MODE) {
+ if (work->hdr.callback != NULL) {
+ work->hdr.callback(work->hdr.user_ctx);
+ } else {
+#ifdef RNG_ENTROPY_DEBUG
+ LOG_KDIAG_ARGS
+ ("Callback ptr for %p is NULL",
+ work);
+#endif
+ }
+ }
+ }
+ } /* while */
+
+#ifdef RNG_ENTROPY_DEBUG
+ LOG_KDIAG("entropy task ending");
+#endif
+
+ os_dev_task_return(OS_ERROR_OK_S);
+} /* rng_entropy_task */
+
+#ifdef FSL_HAVE_RNGA
+/*****************************************************************************/
+/* fn rng_sec_failure() */
+/*****************************************************************************/
+/*!
+ * Function to handle "Security Alarm" indication from SCC.
+ *
+ * This function is registered with the Security Monitor ans the callback
+ * function for the RNG driver. Upon alarm, it will shut down the driver so
+ * that no more random data can be retrieved.
+ *
+ * @return void
+ */
+static void rng_sec_failure(void)
+{
+ os_printk(KERN_ALERT "RNG Driver: Security Failure Alarm received.\n");
+
+ rng_cleanup();
+
+ return;
+}
+#endif
+
+#ifdef RNG_REGISTER_DEBUG
+/*****************************************************************************/
+/* fn dbg_rng_read_register() */
+/*****************************************************************************/
+/*!
+ * Noisily read a 32-bit value to an RNG register.
+ * @param offset The address of the register to read.
+ *
+ * @return The register value
+ * */
+static uint32_t dbg_rng_read_register(uint32_t offset)
+{
+ uint32_t value;
+
+ value = os_read32(rng_base + offset);
+#ifndef RNG_ENTROPY_DEBUG
+ if (offset != RNG_OUTPUT_FIFO) {
+#endif
+ pr_debug("RNG RD: 0x%4x : 0x%08x\n", offset, value);
+#ifndef RNG_ENTROPY_DEBUG
+ }
+#endif
+ return value;
+}
+
+/*****************************************************************************/
+/* fn dbg_rng_write_register() */
+/*****************************************************************************/
+/*!
+ * Noisily write a 32-bit value to an RNG register.
+ * @param offset The address of the register to written.
+ *
+ * @param value The new register value
+ */
+static void dbg_rng_write_register(uint32_t offset, uint32_t value)
+{
+ LOG_KDIAG_ARGS("WR: 0x%4x : 0x%08x", offset, value);
+ os_write32(value, rng_base + offset);
+ return;
+}
+
+#endif /* RNG_REGISTER_DEBUG */
diff --git a/drivers/mxc/security/rng/shw_driver.c b/drivers/mxc/security/rng/shw_driver.c
new file mode 100644
index 000000000000..a6e80a0d4eb0
--- /dev/null
+++ b/drivers/mxc/security/rng/shw_driver.c
@@ -0,0 +1,2335 @@
+/*
+ * Copyright (C) 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*! @file shw_driver.c
+ *
+ * This is the user-mode driver code for the FSL Security Hardware (SHW) API.
+ * as well as the 'common' FSL SHW API code for kernel API users.
+ *
+ * Its interaction with the Linux kernel is from calls to shw_init() when the
+ * driver is loaded, and shw_shutdown() should the driver be unloaded.
+ *
+ * The User API (driver interface) is handled by the following functions:
+ * @li shw_open() - handles open() system call on FSL SHW device
+ * @li shw_release() - handles close() system call on FSL SHW device
+ * @li shw_ioctl() - handles ioctl() system call on FSL SHW device
+ *
+ * The driver also provides the following functions for kernel users of the FSL
+ * SHW API:
+ * @li fsl_shw_register_user()
+ * @li fsl_shw_deregister_user()
+ * @li fsl_shw_get_capabilities()
+ * @li fsl_shw_get_results()
+ *
+ * All other functions are internal to the driver.
+ *
+ * The life of the driver starts at boot (or module load) time, with a call by
+ * the kernel to shw_init().
+ *
+ * The life of the driver ends when the kernel is shutting down (or the driver
+ * is being unloaded). At this time, shw_shutdown() is called. No function
+ * will ever be called after that point.
+ *
+ * In the case that the driver is reloaded, a new copy of the driver, with
+ * fresh global values, etc., is loaded, and there will be a new call to
+ * shw_init().
+ *
+ * In user mode, the user's fsl_shw_register_user() call causes an open() event
+ * on the driver followed by a ioctl() with the registration information. Any
+ * subsequent API calls by the user are handled through the ioctl() function
+ * and shuffled off to the appropriate routine (or driver) for service. The
+ * fsl_shw_deregister_user() call by the user results in a close() function
+ * call on the driver.
+ *
+ * In kernel mode, the driver provides the functions fsl_shw_register_user(),
+ * fsl_shw_deregister_user(), fsl_shw_get_capabilities(), and
+ * fsl_shw_get_results(). Other parts of the API are provided by other
+ * drivers, if available, to support the cryptographic functions.
+ */
+
+#include "portable_os.h"
+#include "fsl_shw.h"
+#include "fsl_shw_keystore.h"
+
+#include "shw_internals.h"
+
+#ifdef FSL_HAVE_SCC2
+#include <linux/mxc_scc2_driver.h>
+#else
+#include <linux/mxc_scc_driver.h>
+#endif
+
+#ifdef SHW_DEBUG
+#include <diagnostic.h>
+#endif
+
+/******************************************************************************
+ *
+ * Function Declarations
+ *
+ *****************************************************************************/
+
+/* kernel interface functions */
+OS_DEV_INIT_DCL(shw_init);
+OS_DEV_SHUTDOWN_DCL(shw_shutdown);
+OS_DEV_IOCTL_DCL(shw_ioctl);
+OS_DEV_MMAP_DCL(shw_mmap);
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_smalloc);
+EXPORT_SYMBOL(fsl_shw_sfree);
+EXPORT_SYMBOL(fsl_shw_sstatus);
+EXPORT_SYMBOL(fsl_shw_diminish_perms);
+EXPORT_SYMBOL(do_scc_encrypt_region);
+EXPORT_SYMBOL(do_scc_decrypt_region);
+
+EXPORT_SYMBOL(do_system_keystore_slot_alloc);
+EXPORT_SYMBOL(do_system_keystore_slot_dealloc);
+EXPORT_SYMBOL(do_system_keystore_slot_load);
+EXPORT_SYMBOL(do_system_keystore_slot_encrypt);
+EXPORT_SYMBOL(do_system_keystore_slot_decrypt);
+#endif
+
+static os_error_code
+shw_handle_scc_sfree(fsl_shw_uco_t * user_ctx, uint32_t info);
+
+static os_error_code
+shw_handle_scc_sstatus(fsl_shw_uco_t * user_ctx, uint32_t info);
+
+static os_error_code
+shw_handle_scc_drop_perms(fsl_shw_uco_t * user_ctx, uint32_t info);
+
+static os_error_code
+shw_handle_scc_encrypt(fsl_shw_uco_t * user_ctx, uint32_t info);
+
+static os_error_code
+shw_handle_scc_decrypt(fsl_shw_uco_t * user_ctx, uint32_t info);
+
+#ifdef FSL_HAVE_SCC2
+static fsl_shw_return_t register_user_partition(fsl_shw_uco_t * user_ctx,
+ uint32_t user_base,
+ void *kernel_base);
+static fsl_shw_return_t deregister_user_partition(fsl_shw_uco_t * user_ctx,
+ uint32_t user_base);
+void *lookup_user_partition(fsl_shw_uco_t * user_ctx, uint32_t user_base);
+
+#endif /* FSL_HAVE_SCC2 */
+
+/******************************************************************************
+ *
+ * Global / Static Variables
+ *
+ *****************************************************************************/
+
+/*!
+ * Major node (user/device interaction value) of this driver.
+ */
+static int shw_major_node = SHW_MAJOR_NODE;
+
+/*!
+ * Flag to know whether the driver has been associated with its user device
+ * node (e.g. /dev/shw).
+ */
+static int shw_device_registered = 0;
+
+/*!
+ * OS-dependent handle used for registering user interface of a driver.
+ */
+static os_driver_reg_t reg_handle;
+
+/*!
+ * Linked List of registered users of the API
+ */
+fsl_shw_uco_t *user_list;
+
+/*!
+ * This is the lock for all user request pools. H/W component drivers may also
+ * use it for their own work queues.
+ */
+os_lock_t shw_queue_lock = NULL;
+
+/* This is the system keystore object */
+fsl_shw_kso_t system_keystore;
+
+#ifndef FSL_HAVE_SAHARA
+/*! Empty list of supported symmetric algorithms. */
+static fsl_shw_key_alg_t pf_syms[] = {
+};
+
+/*! Empty list of supported symmetric modes. */
+static fsl_shw_sym_mode_t pf_modes[] = {
+};
+
+/*! Empty list of supported hash algorithms. */
+static fsl_shw_hash_alg_t pf_hashes[] = {
+};
+#endif /* no Sahara */
+
+/*! This matches SHW capabilities... */
+static fsl_shw_pco_t cap = {
+ 1, 3, /* api version number - major & minor */
+ 2, 3, /* driver version number - major & minor */
+ sizeof(pf_syms) / sizeof(fsl_shw_key_alg_t), /* key alg count */
+ pf_syms, /* key alg list ptr */
+ sizeof(pf_modes) / sizeof(fsl_shw_sym_mode_t), /* sym mode count */
+ pf_modes, /* modes list ptr */
+ sizeof(pf_hashes) / sizeof(fsl_shw_hash_alg_t), /* hash alg count */
+ pf_hashes, /* hash list ptr */
+ /*
+ * The following table must be set to handle all values of key algorithm
+ * and sym mode, and be in the correct order..
+ */
+ { /* Stream, ECB, CBC, CTR */
+ {0, 0, 0, 0}
+ , /* HMAC */
+ {0, 0, 0, 0}
+ , /* AES */
+ {0, 0, 0, 0}
+ , /* DES */
+#ifdef FSL_HAVE_DRYICE
+ {0, 1, 1, 0}
+ , /* 3DES - ECB and CBC */
+#else
+ {0, 0, 0, 0}
+ , /* 3DES */
+#endif
+ {0, 0, 0, 0} /* ARC4 */
+ }
+ ,
+ 0, 0, /* SCC driver version */
+ 0, 0, 0, /* SCC version/capabilities */
+ {{0, 0}
+ }
+ , /* (filled in during OS_INIT) */
+};
+
+/* These are often handy */
+#ifndef FALSE
+/*! Not true. Guaranteed to be zero. */
+#define FALSE 0
+#endif
+#ifndef TRUE
+/*! True. Guaranteed to be non-zero. */
+#define TRUE 1
+#endif
+
+/******************************************************************************
+ *
+ * Function Implementations - Externally Accessible
+ *
+ *****************************************************************************/
+
+/*****************************************************************************/
+/* fn shw_init() */
+/*****************************************************************************/
+/*!
+ * Initialize the driver.
+ *
+ * This routine is called during kernel init or module load (insmod).
+ *
+ * @return OS_ERROR_OK_S on success, errno on failure
+ */
+OS_DEV_INIT(shw_init)
+{
+ os_error_code error_code = OS_ERROR_NO_MEMORY_S; /* assume failure */
+ scc_config_t *shw_capabilities;
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW Driver: Loading");
+#endif
+
+ user_list = NULL;
+ shw_queue_lock = os_lock_alloc_init();
+
+ if (shw_queue_lock != NULL) {
+ error_code = shw_setup_user_driver_interaction();
+ if (error_code != OS_ERROR_OK_S) {
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS
+ ("SHW Driver: Failed to setup user i/f: %d",
+ error_code);
+#endif
+ }
+ }
+
+ /* queue_lock not NULL */
+ /* Fill in the SCC portion of the capabilities object */
+ shw_capabilities = scc_get_configuration();
+ cap.scc_driver_major = shw_capabilities->driver_major_version;
+ cap.scc_driver_minor = shw_capabilities->driver_minor_version;
+ cap.scm_version = shw_capabilities->scm_version;
+ cap.smn_version = shw_capabilities->smn_version;
+ cap.block_size_bytes = shw_capabilities->block_size_bytes;
+
+#ifdef FSL_HAVE_SCC
+ cap.u.scc_info.black_ram_size_blocks =
+ shw_capabilities->black_ram_size_blocks;
+ cap.u.scc_info.red_ram_size_blocks =
+ shw_capabilities->red_ram_size_blocks;
+#elif defined(FSL_HAVE_SCC2)
+ cap.u.scc2_info.partition_size_bytes =
+ shw_capabilities->partition_size_bytes;
+ cap.u.scc2_info.partition_count = shw_capabilities->partition_count;
+#endif
+
+#if defined(FSL_HAVE_SCC2) || defined(FSL_HAVE_DRYICE)
+ if (error_code == OS_ERROR_OK_S) {
+ /* set up the system keystore, using the default keystore handler */
+ fsl_shw_init_keystore_default(&system_keystore);
+
+ if (fsl_shw_establish_keystore(NULL, &system_keystore)
+ == FSL_RETURN_OK_S) {
+ error_code = OS_ERROR_OK_S;
+ } else {
+ error_code = OS_ERROR_FAIL_S;
+ }
+
+ if (error_code != OS_ERROR_OK_S) {
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS
+ ("Registering the system keystore failed with error"
+ " code: %d\n", error_code);
+#endif
+ }
+ }
+#endif /* FSL_HAVE_SCC2 */
+
+ if (error_code != OS_ERROR_OK_S) {
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHW: Driver initialization failed. %d",
+ error_code);
+#endif
+ shw_cleanup();
+ } else {
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: Driver initialization complete.");
+#endif
+ }
+
+ os_dev_init_return(error_code);
+} /* shw_init */
+
+/*****************************************************************************/
+/* fn shw_shutdown() */
+/*****************************************************************************/
+/*!
+ * Prepare driver for exit.
+ *
+ * This is called during @c rmmod when the driver is unloading or when the
+ * kernel is shutting down.
+ *
+ * Calls shw_cleanup() to do all work to undo anything that happened during
+ * initialization or while driver was running.
+ */
+OS_DEV_SHUTDOWN(shw_shutdown)
+{
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: shutdown called");
+#endif
+ shw_cleanup();
+
+ os_dev_shutdown_return(OS_ERROR_OK_S);
+} /* shw_shutdown */
+
+/*****************************************************************************/
+/* fn shw_cleanup() */
+/*****************************************************************************/
+/*!
+ * Prepare driver for shutdown.
+ *
+ * Remove the driver registration.
+ *
+ */
+static void shw_cleanup(void)
+{
+ if (shw_device_registered) {
+
+ /* Turn off the all association with OS */
+ os_driver_remove_registration(reg_handle);
+ shw_device_registered = 0;
+ }
+
+ if (shw_queue_lock != NULL) {
+ os_lock_deallocate(shw_queue_lock);
+ }
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW Driver: Cleaned up");
+#endif
+} /* shw_cleanup */
+
+/*****************************************************************************/
+/* fn shw_open() */
+/*****************************************************************************/
+/*!
+ * Handle @c open() call from user.
+ *
+ * @return OS_ERROR_OK_S on success (always!)
+ */
+OS_DEV_OPEN(shw_open)
+{
+ os_error_code status = OS_ERROR_OK_S;
+
+ os_dev_set_user_private(NULL); /* Make sure */
+
+ os_dev_open_return(status);
+} /* shw_open */
+
+/*****************************************************************************/
+/* fn shw_ioctl() */
+/*****************************************************************************/
+/*!
+ * Process an ioctl() request from user-mode API.
+ *
+ * This code determines which of the API requests the user has made and then
+ * sends the request off to the appropriate function.
+ *
+ * @return ioctl_return()
+ */
+OS_DEV_IOCTL(shw_ioctl)
+{
+ os_error_code code = OS_ERROR_FAIL_S;
+
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHW: IOCTL %d received", os_dev_get_ioctl_op());
+#endif
+ switch (os_dev_get_ioctl_op()) {
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_REGISTER_USER:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: register_user ioctl received");
+#endif
+ {
+ fsl_shw_uco_t *user_ctx =
+ os_alloc_memory(sizeof(*user_ctx), 0);
+
+ if (user_ctx == NULL) {
+ code = OS_ERROR_NO_MEMORY_S;
+ } else {
+ code =
+ init_uco(user_ctx,
+ (fsl_shw_uco_t *)
+ os_dev_get_ioctl_arg());
+ if (code == OS_ERROR_OK_S) {
+ os_dev_set_user_private(user_ctx);
+ } else {
+ os_free_memory(user_ctx);
+ }
+ }
+ }
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_DEREGISTER_USER:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: deregister_user ioctl received");
+#endif
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+ SHW_REMOVE_USER(user_ctx);
+ }
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_GET_RESULTS:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: get_results ioctl received");
+#endif
+ code = get_results(user_ctx,
+ (struct results_req *)
+ os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_GET_CAPABILITIES:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: get_capabilities ioctl received");
+#endif
+ code = get_capabilities(user_ctx,
+ (fsl_shw_pco_t *)
+ os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_GET_RANDOM:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: get_random ioctl received");
+#endif
+ code = get_random(user_ctx,
+ (struct get_random_req *)
+ os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_ADD_ENTROPY:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: add_entropy ioctl received");
+#endif
+ code = add_entropy(user_ctx,
+ (struct add_entropy_req *)
+ os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_DROP_PERMS:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: drop permissions ioctl received");
+#endif
+ code =
+ shw_handle_scc_drop_perms(user_ctx, os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_SSTATUS:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: sstatus ioctl received");
+#endif
+ code = shw_handle_scc_sstatus(user_ctx, os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_SFREE:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: sfree ioctl received");
+#endif
+ code = shw_handle_scc_sfree(user_ctx, os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_SCC_ENCRYPT:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: scc encrypt ioctl received");
+#endif
+ code = shw_handle_scc_encrypt(user_ctx, os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_SCC_DECRYPT:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: scc decrypt ioctl received");
+#endif
+ code = shw_handle_scc_decrypt(user_ctx, os_dev_get_ioctl_arg());
+ break;
+
+ default:
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHW: Unexpected ioctl %d",
+ os_dev_get_ioctl_op());
+#endif
+ break;
+ }
+
+ os_dev_ioctl_return(code);
+}
+
+#ifdef FSL_HAVE_SCC2
+
+/*****************************************************************************/
+/* fn get_user_smid() */
+/*****************************************************************************/
+uint32_t get_user_smid(void *proc)
+{
+ /*
+ * A real implementation would have some way to handle signed applications
+ * which wouild be assigned distinct SMIDs. For the reference
+ * implementation, we show where this would be determined (here), but
+ * always provide a fixed answer, thus not separating users at all.
+ */
+
+ return 0x42eaae42;
+}
+
+/* user_base: userspace base address of the partition
+ * kernel_base: kernel mode base address of the partition
+ */
+static fsl_shw_return_t register_user_partition(fsl_shw_uco_t * user_ctx,
+ uint32_t user_base,
+ void *kernel_base)
+{
+ fsl_shw_spo_t *partition_info;
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ if (user_ctx == NULL) {
+ goto out;
+ }
+
+ partition_info = os_alloc_memory(sizeof(fsl_shw_spo_t), GFP_KERNEL);
+
+ if (partition_info == NULL) {
+ goto out;
+ }
+
+ /* stuff the partition info, then put it at the front of the chain */
+ partition_info->user_base = user_base;
+ partition_info->kernel_base = kernel_base;
+ partition_info->next = user_ctx->partition;
+
+ user_ctx->partition = (struct fsl_shw_spo_t *)partition_info;
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS
+ ("partition with user_base=%p, kernel_base=%p registered.",
+ (void *)user_base, kernel_base);
+#endif
+
+ ret = FSL_RETURN_OK_S;
+
+ out:
+
+ return ret;
+}
+
+/* if the partition is in the users list, remove it */
+static fsl_shw_return_t deregister_user_partition(fsl_shw_uco_t * user_ctx,
+ uint32_t user_base)
+{
+ fsl_shw_spo_t *curr = (fsl_shw_spo_t *) user_ctx->partition;
+ fsl_shw_spo_t *last = (fsl_shw_spo_t *) user_ctx->partition;
+
+ while (curr != NULL) {
+ if (curr->user_base == user_base) {
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS
+ ("deregister_user_partition: partition with "
+ "user_base=%p, kernel_base=%p deregistered.\n",
+ (void *)curr->user_base, curr->kernel_base);
+#endif
+
+ if (last == curr) {
+ user_ctx->partition = curr->next;
+ os_free_memory(curr);
+ return FSL_RETURN_OK_S;
+ } else {
+ last->next = curr->next;
+ os_free_memory(curr);
+ return FSL_RETURN_OK_S;
+ }
+ }
+ last = curr;
+ curr = (fsl_shw_spo_t *) curr->next;
+ }
+
+ return FSL_RETURN_ERROR_S;
+}
+
+/* Find the kernel-mode address of the partition.
+ * This can then be passed to the SCC functions.
+ */
+void *lookup_user_partition(fsl_shw_uco_t * user_ctx, uint32_t user_base)
+{
+ /* search through the partition chain to find one that matches the user base
+ * address.
+ */
+ fsl_shw_spo_t *curr = (fsl_shw_spo_t *) user_ctx->partition;
+
+ while (curr != NULL) {
+ if (curr->user_base == user_base) {
+ return curr->kernel_base;
+ }
+ curr = (fsl_shw_spo_t *) curr->next;
+ }
+ return NULL;
+}
+
+#endif /* FSL_HAVE_SCC2 */
+
+/*!
+*******************************************************************************
+* This function implements the smalloc() function for userspace programs, by
+* making a call to the SCC2 mmap() function that acquires a region of secure
+* memory on behalf of the user, and then maps it into the users memory space.
+* Currently, the only memory size supported is that of a single SCC2 partition.
+* Requests for other sized memory regions will fail.
+*/
+OS_DEV_MMAP(shw_mmap)
+{
+ os_error_code status = OS_ERROR_NO_MEMORY_S;
+
+#ifdef FSL_HAVE_SCC2
+ {
+ scc_return_t scc_ret;
+ fsl_shw_return_t fsl_ret;
+ uint32_t partition_registered = FALSE;
+
+ uint32_t user_base;
+ void *partition_base;
+ uint32_t smid;
+ scc_config_t *scc_configuration;
+
+ int part_no = -1;
+ uint32_t part_phys;
+
+ fsl_shw_uco_t *user_ctx =
+ (fsl_shw_uco_t *) os_dev_get_user_private();
+
+ /* Make sure that the user context is valid */
+ if (user_ctx == NULL) {
+ user_ctx =
+ os_alloc_memory(sizeof(*user_ctx), GFP_KERNEL);
+
+ if (user_ctx == NULL) {
+ status = OS_ERROR_NO_MEMORY_S;
+ goto out;
+ }
+ fsl_shw_register_user(user_ctx);
+ os_dev_set_user_private(user_ctx);
+ }
+
+ /* Determine the size of a secure partition */
+ scc_configuration = scc_get_configuration();
+
+ /* Check that the memory size requested is equal to the partition
+ * size, and that the requested destination is on a page boundary.
+ */
+ if (((os_mmap_user_base() % PAGE_SIZE) != 0) ||
+ (os_mmap_memory_size() !=
+ scc_configuration->partition_size_bytes)) {
+ status = OS_ERROR_BAD_ARG_S;
+ goto out;
+ }
+
+ /* Retrieve the SMID associated with the user */
+ smid = get_user_smid(user_ctx->process);
+
+ /* Attempt to allocate a secure partition */
+ scc_ret =
+ scc_allocate_partition(smid, &part_no, &partition_base,
+ &part_phys);
+ if (scc_ret != SCC_RET_OK) {
+ pr_debug
+ ("SCC mmap() request failed to allocate partition;"
+ " error %d\n", status);
+ status = OS_ERROR_FAIL_S;
+ goto out;
+ }
+
+ pr_debug("scc_mmap() acquired partition %d at %08x\n",
+ part_no, part_phys);
+
+ /* Record partition info in the user context */
+ user_base = os_mmap_user_base();
+ fsl_ret =
+ register_user_partition(user_ctx, user_base,
+ partition_base);
+
+ if (fsl_ret != FSL_RETURN_OK_S) {
+ pr_debug
+ ("SCC mmap() request failed to register partition with user"
+ " context, error: %d\n", fsl_ret);
+ status = OS_ERROR_FAIL_S;
+ }
+
+ partition_registered = TRUE;
+
+ status = map_user_memory(os_mmap_memory_ctx(), part_phys,
+ os_mmap_memory_size());
+
+#ifdef SHW_DEBUG
+ if (status == OS_ERROR_OK_S) {
+ LOG_KDIAG_ARGS
+ ("Partition allocated: user_base=%p, partition_base=%p.",
+ (void *)user_base, partition_base);
+ }
+#endif
+
+ out:
+ /* If there is an error it has to be handled here */
+ if (status != OS_ERROR_OK_S) {
+ /* if the partition was registered with the user, unregister it. */
+ if (partition_registered == TRUE) {
+ deregister_user_partition(user_ctx, user_base);
+ }
+
+ /* if the partition was allocated, deallocate it */
+ if (partition_base != NULL) {
+ scc_release_partition(partition_base);
+ }
+ }
+ }
+#endif /* FSL_HAVE_SCC2 */
+
+ return status;
+}
+
+/*****************************************************************************/
+/* fn shw_release() */
+/*****************************************************************************/
+/*!
+ * Handle @c close() call from user.
+ * This is a Linux device driver interface routine.
+ *
+ * @return OS_ERROR_OK_S on success (always!)
+ */
+OS_DEV_CLOSE(shw_release)
+{
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+ os_error_code code = OS_ERROR_OK_S;
+
+ if (user_ctx != NULL) {
+
+ fsl_shw_deregister_user(user_ctx);
+ os_free_memory(user_ctx);
+ os_dev_set_user_private(NULL);
+
+ }
+
+ os_dev_close_return(code);
+} /* shw_release */
+
+/*****************************************************************************/
+/* fn shw_user_callback() */
+/*****************************************************************************/
+/*!
+ * FSL SHW User callback function.
+ *
+ * This function is set in the kernel version of the user context as the
+ * callback function when the user mode user wants a callback. Its job is to
+ * inform the user process that results (may) be available. It does this by
+ * sending a SIGUSR2 signal which is then caught by the user-mode FSL SHW
+ * library.
+ *
+ * @param user_ctx Kernel version of uco associated with the request.
+ *
+ * @return void
+ */
+static void shw_user_callback(fsl_shw_uco_t * user_ctx)
+{
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHW: Signalling callback user process for context %p\n",
+ user_ctx);
+#endif
+ os_send_signal(user_ctx->process, SIGUSR2);
+}
+
+/*****************************************************************************/
+/* fn setup_user_driver_interaction() */
+/*****************************************************************************/
+/*!
+ * Register the driver with the kernel as the driver for shw_major_node. Note
+ * that this value may be zero, in which case the major number will be assigned
+ * by the OS. shw_major_node is never modified.
+ *
+ * The open(), ioctl(), and close() handles for the driver ned to be registered
+ * with the kernel. Upon success, shw_device_registered will be true;
+ *
+ * @return OS_ERROR_OK_S on success, or an os err code
+ */
+static os_error_code shw_setup_user_driver_interaction(void)
+{
+ os_error_code error_code;
+
+ os_driver_init_registration(reg_handle);
+ os_driver_add_registration(reg_handle, OS_FN_OPEN,
+ OS_DEV_OPEN_REF(shw_open));
+ os_driver_add_registration(reg_handle, OS_FN_IOCTL,
+ OS_DEV_IOCTL_REF(shw_ioctl));
+ os_driver_add_registration(reg_handle, OS_FN_CLOSE,
+ OS_DEV_CLOSE_REF(shw_release));
+ os_driver_add_registration(reg_handle, OS_FN_MMAP,
+ OS_DEV_MMAP_REF(shw_mmap));
+ error_code = os_driver_complete_registration(reg_handle, shw_major_node,
+ SHW_DRIVER_NAME);
+
+ if (error_code != OS_ERROR_OK_S) {
+ /* failure ! */
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHW Driver: register device driver failed: %d",
+ error_code);
+#endif
+ } else { /* success */
+ shw_device_registered = TRUE;
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHW Driver: Major node is %d\n",
+ os_driver_get_major(reg_handle));
+#endif
+ }
+
+ return error_code;
+} /* shw_setup_user_driver_interaction */
+
+/******************************************************************/
+/* User Mode Support */
+/******************************************************************/
+
+/*!
+ * Initialze kernel User Context Object from User-space version.
+ *
+ * Copy user UCO into kernel UCO, set flags and fields for operation
+ * within kernel space. Add user to driver's list of users.
+ *
+ * @param user_ctx Pointer to kernel space UCO
+ * @param user_mode_uco User pointer to user space version
+ *
+ * @return os_error_code
+ */
+static os_error_code init_uco(fsl_shw_uco_t * user_ctx, void *user_mode_uco)
+{
+ os_error_code code;
+
+ code = os_copy_from_user(user_ctx, user_mode_uco, sizeof(*user_ctx));
+ if (code == OS_ERROR_OK_S) {
+ user_ctx->flags |= FSL_UCO_USERMODE_USER;
+ user_ctx->result_pool.head = NULL;
+ user_ctx->result_pool.tail = NULL;
+ user_ctx->process = os_get_process_handle();
+ user_ctx->callback = shw_user_callback;
+ SHW_ADD_USER(user_ctx);
+ }
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHW: init uco returning %d (flags %x)",
+ code, user_ctx->flags);
+#endif
+
+ return code;
+}
+
+/*!
+ * Copy array from kernel to user space.
+ *
+ * This routine will check bounds before trying to copy, and return failure
+ * on bounds violation or error during the copy.
+ *
+ * @param userloc Location in userloc to place data. If NULL, the function
+ * will do nothing (except return NULL).
+ * @param userend Address beyond allowed copy region at @c userloc.
+ * @param data_start Location of data to be copied
+ * @param element_size sizeof() an element
+ * @param element_count Number of elements of size element_size to copy.
+ * @return New value of userloc, or NULL if there was an error.
+ */
+inline static void *copy_array(void *userloc, void *userend, void *data_start,
+ unsigned element_size, unsigned element_count)
+{
+ unsigned byte_count = element_size * element_count;
+
+ if ((userloc == NULL) || (userend == NULL)
+ || ((userloc + byte_count) >= userend) ||
+ (copy_to_user(userloc, data_start, byte_count) != OS_ERROR_OK_S)) {
+ userloc = NULL;
+ } else {
+ userloc += byte_count;
+ }
+
+ return userloc;
+}
+
+/*!
+ * Send an FSL SHW API return code up into the user-space request structure.
+ *
+ * @param user_header User address of request block / request header
+ * @param result_code The FSL SHW API code to be placed at header.code
+ *
+ * @return an os_error_code
+ *
+ * NOTE: This function assumes that the shw_req_header is at the beginning of
+ * each request structure.
+ */
+inline static os_error_code copy_fsl_code(void *user_header,
+ fsl_shw_return_t result_code)
+{
+ return os_copy_to_user(user_header +
+ offsetof(struct shw_req_header, code),
+ &result_code, sizeof(result_code));
+}
+
+static os_error_code shw_handle_scc_drop_perms(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code status = OS_ERROR_NO_MEMORY_S;
+#ifdef FSL_HAVE_SCC2
+ scc_return_t scc_ret;
+ scc_partition_info_t partition_info;
+ void *kernel_base;
+
+ status =
+ os_copy_from_user(&partition_info, (void *)info,
+ sizeof(partition_info));
+
+ if (status != OS_ERROR_OK_S) {
+ goto out;
+ }
+
+ /* validate that the user owns this partition, and look up its handle */
+ kernel_base = lookup_user_partition(user_ctx, partition_info.user_base);
+
+ if (kernel_base == NULL) {
+ status = OS_ERROR_FAIL_S;
+#ifdef SHW_DEBUG
+ LOG_KDIAG("_scc_drop_perms(): failed to find partition\n");
+#endif
+ goto out;
+ }
+
+ /* call scc driver to perform the drop */
+ scc_ret = scc_diminish_permissions(kernel_base,
+ partition_info.permissions);
+ if (scc_ret == SCC_RET_OK) {
+ status = OS_ERROR_OK_S;
+ } else {
+ status = OS_ERROR_FAIL_S;
+ }
+
+ out:
+#endif /* FSL_HAVE_SCC2 */
+ return status;
+}
+
+static os_error_code shw_handle_scc_sstatus(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code status = OS_ERROR_NO_MEMORY_S;
+#ifdef FSL_HAVE_SCC2
+ scc_partition_info_t partition_info;
+ void *kernel_base;
+
+ status = os_copy_from_user(&partition_info,
+ (void *)info, sizeof(partition_info));
+
+ if (status != OS_ERROR_OK_S) {
+ goto out;
+ }
+
+ /* validate that the user owns this partition, and look up its handle */
+ kernel_base = lookup_user_partition(user_ctx, partition_info.user_base);
+
+ if (kernel_base == NULL) {
+ status = OS_ERROR_FAIL_S;
+#ifdef SHW_DEBUG
+ LOG_KDIAG("Failed to find partition\n");
+#endif
+ goto out;
+ }
+
+ /* Call the SCC driver to ask about the partition status */
+ partition_info.status = scc_partition_status(kernel_base);
+
+ /* and copy the structure out */
+ status = os_copy_to_user((void *)info,
+ &partition_info, sizeof(partition_info));
+
+ out:
+#endif /* FSL_HAVE_SCC2 */
+ return status;
+}
+
+static os_error_code shw_handle_scc_sfree(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code status = OS_ERROR_NO_MEMORY_S;
+#ifdef FSL_HAVE_SCC2
+ {
+ scc_partition_info_t partition_info;
+ void *kernel_base;
+ int ret;
+
+ status = os_copy_from_user(&partition_info,
+ (void *)info,
+ sizeof(partition_info));
+
+ /* check that the copy was successful */
+ if (status != OS_ERROR_OK_S) {
+ goto out;
+ }
+
+ /* validate that the user owns this partition, and look up its handle */
+ kernel_base =
+ lookup_user_partition(user_ctx, partition_info.user_base);
+
+ if (kernel_base == NULL) {
+ status = OS_ERROR_FAIL_S;
+#ifdef SHW_DEBUG
+ LOG_KDIAG("failed to find partition\n");
+#endif /*SHW_DEBUG */
+ goto out;
+ }
+
+ /* Unmap the memory region (see sys_munmap in mmap.c) */
+ ret = unmap_user_memory(partition_info.user_base, 8192);
+
+ /* If the memory was successfully released */
+ if (ret == OS_ERROR_OK_S) {
+
+ /* release the partition */
+ scc_release_partition(kernel_base);
+
+ /* and remove it from the users context */
+ deregister_user_partition(user_ctx,
+ partition_info.user_base);
+
+ status = OS_ERROR_OK_S;
+
+ } else {
+#ifdef SHW_DEBUG
+ LOG_KDIAG("do_munmap not successful!");
+#endif
+ }
+
+ }
+ out:
+#endif /* FSL_HAVE_SCC2 */
+ return status;
+}
+
+static os_error_code shw_handle_scc_encrypt(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code status = OS_ERROR_FAIL_S;
+#ifdef FSL_HAVE_SCC2
+ {
+ fsl_shw_return_t retval;
+ scc_region_t region_info;
+ void *page_ctx = NULL;
+ void *black_addr = NULL;
+ void *partition_base = NULL;
+ scc_config_t *scc_configuration;
+
+ status =
+ os_copy_from_user(&region_info, (void *)info,
+ sizeof(region_info));
+
+ if (status != OS_ERROR_OK_S) {
+ goto out;
+ }
+
+ /* validate that the user owns this partition, and look up its handle */
+ partition_base = lookup_user_partition(user_ctx,
+ region_info.
+ partition_base);
+
+ if (partition_base == NULL) {
+ status = OS_ERROR_FAIL_S;
+#ifdef SHW_DEBUG
+ LOG_KDIAG("failed to find secure partition\n");
+#endif
+ goto out;
+ }
+
+ /* Check that the memory size requested is correct */
+ scc_configuration = scc_get_configuration();
+ if (region_info.offset + region_info.length >
+ scc_configuration->partition_size_bytes) {
+ status = OS_ERROR_FAIL_S;
+ goto out;
+ }
+
+ /* wire down black_data */
+ black_addr = wire_user_memory(region_info.black_data,
+ region_info.length, &page_ctx);
+
+ if (black_addr == NULL) {
+ status = OS_ERROR_FAIL_S;
+ goto out;
+ }
+
+ retval =
+ do_scc_encrypt_region(NULL, partition_base,
+ region_info.offset,
+ region_info.length, black_addr,
+ region_info.IV,
+ region_info.cypher_mode);
+
+ if (retval == FSL_RETURN_OK_S) {
+ status = OS_ERROR_OK_S;
+ } else {
+ status = OS_ERROR_FAIL_S;
+ }
+
+ /* release black data */
+ unwire_user_memory(&page_ctx);
+ }
+ out:
+
+#endif /* FSL_HAVE_SCC2 */
+ return status;
+}
+
+static os_error_code shw_handle_scc_decrypt(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code status = OS_ERROR_FAIL_S;
+#ifdef FSL_HAVE_SCC2
+ {
+ fsl_shw_return_t retval;
+ scc_region_t region_info;
+ void *page_ctx = NULL;
+ void *black_addr;
+ void *partition_base;
+ scc_config_t *scc_configuration;
+
+ status =
+ os_copy_from_user(&region_info, (void *)info,
+ sizeof(region_info));
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS
+ ("partition_base: %p, offset: %i, length: %i, black data: %p",
+ (void *)region_info.partition_base, region_info.offset,
+ region_info.length, (void *)region_info.black_data);
+#endif
+
+ if (status != OS_ERROR_OK_S) {
+ goto out;
+ }
+
+ /* validate that the user owns this partition, and look up its handle */
+ partition_base = lookup_user_partition(user_ctx,
+ region_info.
+ partition_base);
+
+ if (partition_base == NULL) {
+ status = OS_ERROR_FAIL_S;
+#ifdef SHW_DEBUG
+ LOG_KDIAG("failed to find partition\n");
+#endif
+ goto out;
+ }
+
+ /* Check that the memory size requested is correct */
+ scc_configuration = scc_get_configuration();
+ if (region_info.offset + region_info.length >
+ scc_configuration->partition_size_bytes) {
+ status = OS_ERROR_FAIL_S;
+ goto out;
+ }
+
+ /* wire down black_data */
+ black_addr = wire_user_memory(region_info.black_data,
+ region_info.length, &page_ctx);
+
+ if (black_addr == NULL) {
+ status = OS_ERROR_FAIL_S;
+ goto out;
+ }
+
+ retval =
+ do_scc_decrypt_region(NULL, partition_base,
+ region_info.offset,
+ region_info.length, black_addr,
+ region_info.IV,
+ region_info.cypher_mode);
+
+ if (retval == FSL_RETURN_OK_S) {
+ status = OS_ERROR_OK_S;
+ } else {
+ status = OS_ERROR_FAIL_S;
+ }
+
+ /* release black data */
+ unwire_user_memory(&page_ctx);
+ }
+ out:
+
+#endif /* FSL_HAVE_SCC2 */
+ return status;
+}
+
+fsl_shw_return_t do_system_keystore_slot_alloc(fsl_shw_uco_t * user_ctx,
+ uint32_t key_length,
+ uint64_t ownerid,
+ uint32_t * slot)
+{
+ (void)user_ctx;
+ return keystore_slot_alloc(&system_keystore, key_length, ownerid, slot);
+}
+
+fsl_shw_return_t do_system_keystore_slot_dealloc(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot)
+{
+ (void)user_ctx;
+ return keystore_slot_dealloc(&system_keystore, ownerid, slot);
+}
+
+fsl_shw_return_t do_system_keystore_slot_load(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ const uint8_t * key,
+ uint32_t key_length)
+{
+ (void)user_ctx;
+ return keystore_slot_load(&system_keystore, ownerid, slot,
+ (void *)key, key_length);
+}
+
+fsl_shw_return_t do_system_keystore_slot_encrypt(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ uint8_t * black_data)
+{
+ (void)user_ctx;
+ return keystore_slot_encrypt(NULL, &system_keystore, ownerid,
+ slot, key_length, black_data);
+}
+
+fsl_shw_return_t do_system_keystore_slot_decrypt(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ const uint8_t * black_data)
+{
+ (void)user_ctx;
+ return keystore_slot_decrypt(NULL, &system_keystore, ownerid,
+ slot, key_length, black_data);
+}
+
+fsl_shw_return_t do_system_keystore_slot_read(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ uint8_t * key_data)
+{
+ (void)user_ctx;
+
+ return keystore_slot_read(&system_keystore, ownerid,
+ slot, key_length, key_data);
+}
+
+/*!
+ * Handle user-mode Get Capabilities request
+ *
+ * Right now, this function can only have a failure if the user has failed to
+ * provide a pointer to a location in user space with enough room to hold the
+ * fsl_shw_pco_t structure and any associated data. It will treat this failure
+ * as an ioctl failure and return an ioctl error code, instead of treating it
+ * as an API failure.
+ *
+ * @param user_ctx The kernel version of user's context
+ * @param user_mode_pco_request Pointer to user-space request
+ *
+ * @return an os_error_code
+ */
+static os_error_code get_capabilities(fsl_shw_uco_t * user_ctx,
+ void *user_mode_pco_request)
+{
+ os_error_code code;
+ struct capabilities_req req;
+ fsl_shw_pco_t local_cap;
+
+ memcpy(&local_cap, &cap, sizeof(cap));
+ /* Initialize pointers to out-of-struct arrays */
+ local_cap.sym_algorithms = NULL;
+ local_cap.sym_modes = NULL;
+ local_cap.sym_modes = NULL;
+
+ code = os_copy_from_user(&req, user_mode_pco_request, sizeof(req));
+ if (code == OS_ERROR_OK_S) {
+ void *endcap;
+ void *user_bounds;
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHE: Received get_cap request: 0x%p/%u/0x%x",
+ req.capabilities, req.size,
+ sizeof(fsl_shw_pco_t));
+#endif
+ endcap = req.capabilities + 1; /* point to end of structure */
+ user_bounds = (void *)req.capabilities + req.size; /* end of area */
+
+ /* First verify that request is big enough for the main structure */
+ if (endcap >= user_bounds) {
+ endcap = NULL; /* No! */
+ }
+
+ /* Copy any Symmetric Algorithm suppport */
+ if (cap.sym_algorithm_count != 0) {
+ local_cap.sym_algorithms = endcap;
+ endcap =
+ copy_array(endcap, user_bounds, cap.sym_algorithms,
+ sizeof(fsl_shw_key_alg_t),
+ cap.sym_algorithm_count);
+ }
+
+ /* Copy any Symmetric Modes suppport */
+ if (cap.sym_mode_count != 0) {
+ local_cap.sym_modes = endcap;
+ endcap = copy_array(endcap, user_bounds, cap.sym_modes,
+ sizeof(fsl_shw_sym_mode_t),
+ cap.sym_mode_count);
+ }
+
+ /* Copy any Hash Algorithm suppport */
+ if (cap.hash_algorithm_count != 0) {
+ local_cap.hash_algorithms = endcap;
+ endcap =
+ copy_array(endcap, user_bounds, cap.hash_algorithms,
+ sizeof(fsl_shw_hash_alg_t),
+ cap.hash_algorithm_count);
+ }
+
+ /* Now copy up the (possibly modified) main structure */
+ if (endcap != NULL) {
+ code =
+ os_copy_to_user(req.capabilities, &local_cap,
+ sizeof(cap));
+ }
+
+ if (endcap == NULL) {
+ code = OS_ERROR_BAD_ADDRESS_S;
+ }
+
+ /* And return the FSL SHW code in the request structure. */
+ if (code == OS_ERROR_OK_S) {
+ code =
+ copy_fsl_code(user_mode_pco_request,
+ FSL_RETURN_OK_S);
+ }
+ }
+
+ /* code may already be set to an error. This is another error case. */
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHW: get capabilities returning %d", code);
+#endif
+
+ return code;
+}
+
+/*!
+ * Handle user-mode Get Results request
+ *
+ * Get arguments from user space into kernel space, then call
+ * fsl_shw_get_results, and then copy its return code and any results from
+ * kernel space back to user space.
+ *
+ * @param user_ctx The kernel version of user's context
+ * @param user_mode_results_req Pointer to user-space request
+ *
+ * @return an os_error_code
+ */
+static os_error_code get_results(fsl_shw_uco_t * user_ctx,
+ void *user_mode_results_req)
+{
+ os_error_code code;
+ struct results_req req;
+ fsl_shw_result_t *results = NULL;
+ int loop;
+
+ code = os_copy_from_user(&req, user_mode_results_req, sizeof(req));
+ loop = 0;
+
+ if (code == OS_ERROR_OK_S) {
+ results = os_alloc_memory(req.requested * sizeof(*results), 0);
+ if (results == NULL) {
+ code = OS_ERROR_NO_MEMORY_S;
+ }
+ }
+
+ if (code == OS_ERROR_OK_S) {
+ fsl_shw_return_t err =
+ fsl_shw_get_results(user_ctx, req.requested,
+ results, &req.actual);
+
+ /* Send API return code up to user. */
+ code = copy_fsl_code(user_mode_results_req, err);
+
+ if ((code == OS_ERROR_OK_S) && (err == FSL_RETURN_OK_S)) {
+ /* Now copy up the result count */
+ code = os_copy_to_user(user_mode_results_req
+ + offsetof(struct results_req,
+ actual), &req.actual,
+ sizeof(req.actual));
+ if ((code == OS_ERROR_OK_S) && (req.actual != 0)) {
+ /* now copy up the results... */
+ code = os_copy_to_user(req.results, results,
+ req.actual *
+ sizeof(*results));
+ }
+ }
+ }
+
+ if (results != NULL) {
+ os_free_memory(results);
+ }
+
+ return code;
+}
+
+/*!
+ * Process header of user-mode request.
+ *
+ * Mark header as User Mode request. Update UCO's flags and reference fields
+ * with current versions from the header.
+ *
+ * @param user_ctx Pointer to kernel version of UCO.
+ * @param hdr Pointer to common part of user request.
+ *
+ * @return void
+ */
+inline static void process_hdr(fsl_shw_uco_t * user_ctx,
+ struct shw_req_header *hdr)
+{
+ hdr->flags |= FSL_UCO_USERMODE_USER;
+ user_ctx->flags = hdr->flags;
+ user_ctx->user_ref = hdr->user_ref;
+
+ return;
+}
+
+/*!
+ * Handle user-mode Get Random request
+ *
+ * @param user_ctx The kernel version of user's context
+ * @param user_mode_get_random_req Pointer to user-space request
+ *
+ * @return an os_error_code
+ */
+static os_error_code get_random(fsl_shw_uco_t * user_ctx,
+ void *user_mode_get_random_req)
+{
+ os_error_code code;
+ struct get_random_req req;
+
+ code = os_copy_from_user(&req, user_mode_get_random_req, sizeof(req));
+ if (code == OS_ERROR_OK_S) {
+ process_hdr(user_ctx, &req.hdr);
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS
+ ("SHW: get_random() for %d bytes in %sblocking mode",
+ req.size,
+ (req.hdr.flags & FSL_UCO_BLOCKING_MODE) ? "" : "non-");
+#endif
+ req.hdr.code =
+ fsl_shw_get_random(user_ctx, req.size, req.random);
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHW: get_random() returning %d", req.hdr.code);
+#endif
+
+ /* Copy FSL function status back to user */
+ code = copy_fsl_code(user_mode_get_random_req, req.hdr.code);
+ }
+
+ return code;
+}
+
+/*!
+ * Handle user-mode Add Entropy request
+ *
+ * @param user_ctx Pointer to the kernel version of user's context
+ * @param user_mode_add_entropy_req Address of user-space request
+ *
+ * @return an os_error_code
+ */
+static os_error_code add_entropy(fsl_shw_uco_t * user_ctx,
+ void *user_mode_add_entropy_req)
+{
+ os_error_code code;
+ struct add_entropy_req req;
+ uint8_t *local_buffer = NULL;
+
+ code = os_copy_from_user(&req, user_mode_add_entropy_req, sizeof(req));
+ if (code == OS_ERROR_OK_S) {
+ local_buffer = os_alloc_memory(req.size, 0); /* for random */
+ if (local_buffer != NULL) {
+ code =
+ os_copy_from_user(local_buffer, req.entropy,
+ req.size);
+ }
+ if (code == OS_ERROR_OK_S) {
+ req.hdr.code = fsl_shw_add_entropy(user_ctx, req.size,
+ local_buffer);
+
+ code =
+ copy_fsl_code(user_mode_add_entropy_req,
+ req.hdr.code);
+ }
+ }
+
+ if (local_buffer != NULL) {
+ os_free_memory(local_buffer);
+ }
+
+ return code;
+}
+
+/******************************************************************/
+/* End User Mode Support */
+/******************************************************************/
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_register_user);
+#endif
+/* REQ-S2LRD-PINTFC-API-GEN-004 */
+/*
+ * Handle user registration.
+ *
+ * @param user_ctx The user context for the registration.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_register_user(fsl_shw_uco_t * user_ctx)
+{
+ fsl_shw_return_t code = FSL_RETURN_INTERNAL_ERROR_S;
+
+ if ((user_ctx->flags & FSL_UCO_BLOCKING_MODE) &&
+ (user_ctx->flags & FSL_UCO_CALLBACK_MODE)) {
+ code = FSL_RETURN_BAD_FLAG_S;
+ goto error_exit;
+ } else if (user_ctx->pool_size == 0) {
+ code = FSL_RETURN_NO_RESOURCE_S;
+ goto error_exit;
+ } else {
+ user_ctx->result_pool.head = NULL;
+ user_ctx->result_pool.tail = NULL;
+ SHW_ADD_USER(user_ctx);
+ code = FSL_RETURN_OK_S;
+ }
+
+ error_exit:
+ return code;
+}
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_deregister_user);
+#endif
+/* REQ-S2LRD-PINTFC-API-GEN-005 */
+/*!
+ * Destroy the association between the the user and the provider of the API.
+ *
+ * @param user_ctx The user context which is no longer needed.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_deregister_user(fsl_shw_uco_t * user_ctx)
+{
+ shw_queue_entry_t *finished_request;
+ fsl_shw_return_t ret = FSL_RETURN_OK_S;
+
+ /* Clean up what we find in result pool. */
+ do {
+ os_lock_context_t lock_context;
+ os_lock_save_context(shw_queue_lock, lock_context);
+ finished_request = user_ctx->result_pool.head;
+
+ if (finished_request != NULL) {
+ SHW_QUEUE_REMOVE_ENTRY(&user_ctx->result_pool,
+ finished_request);
+ os_unlock_restore_context(shw_queue_lock, lock_context);
+ os_free_memory(finished_request);
+ } else {
+ os_unlock_restore_context(shw_queue_lock, lock_context);
+ }
+ } while (finished_request != NULL);
+
+#ifdef FSL_HAVE_SCC2
+ {
+ fsl_shw_spo_t *partition;
+ struct mm_struct *mm = current->mm;
+
+ while ((user_ctx->partition != NULL)
+ && (ret == FSL_RETURN_OK_S)) {
+
+ partition = user_ctx->partition;
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS
+ ("Found an abandoned secure partition at %p, releasing",
+ partition);
+#endif
+
+ /* It appears that current->mm is not valid if this is called from a
+ * close routine (perhaps only if the program raised an exception that
+ * caused it to close?) If that is the case, then still free the
+ * partition, but do not remove it from the memory space (dangerous?)
+ */
+
+ if (mm == NULL) {
+#ifdef SHW_DEBUG
+ LOG_KDIAG
+ ("Warning: no mm structure found, not unmapping "
+ "partition from user memory\n");
+#endif
+ } else {
+ /* Unmap the memory region (see sys_munmap in mmap.c) */
+ /* Note that this assumes a single memory partition */
+ unmap_user_memory(partition->user_base, 8192);
+ }
+
+ /* If the memory was successfully released */
+ if (ret == OS_ERROR_OK_S) {
+ /* release the partition */
+ scc_release_partition(partition->kernel_base);
+
+ /* and remove it from the users context */
+ deregister_user_partition(user_ctx,
+ partition->user_base);
+
+ ret = FSL_RETURN_OK_S;
+ } else {
+ ret = FSL_RETURN_ERROR_S;
+
+ goto out;
+ }
+ }
+ }
+ out:
+#endif /* FSL_HAVE_SCC2 */
+
+ SHW_REMOVE_USER(user_ctx);
+
+ return ret;
+}
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_get_results);
+#endif
+/* REQ-S2LRD-PINTFC-API-GEN-006 */
+fsl_shw_return_t fsl_shw_get_results(fsl_shw_uco_t * user_ctx,
+ unsigned result_size,
+ fsl_shw_result_t results[],
+ unsigned *result_count)
+{
+ shw_queue_entry_t *finished_request;
+ unsigned loop = 0;
+
+ do {
+ os_lock_context_t lock_context;
+
+ /* Protect state of user's result pool until we have retrieved and
+ * remove the first entry, or determined that the pool is empty. */
+ os_lock_save_context(shw_queue_lock, lock_context);
+ finished_request = user_ctx->result_pool.head;
+
+ if (finished_request != NULL) {
+ uint32_t code = 0;
+
+ SHW_QUEUE_REMOVE_ENTRY(&user_ctx->result_pool,
+ finished_request);
+ os_unlock_restore_context(shw_queue_lock, lock_context);
+
+ results[loop].user_ref = finished_request->user_ref;
+ results[loop].code = finished_request->code;
+ results[loop].detail1 = 0;
+ results[loop].detail2 = 0;
+ results[loop].user_req =
+ finished_request->user_mode_req;
+ if (finished_request->postprocess != NULL) {
+ code =
+ finished_request->
+ postprocess(finished_request);
+ }
+
+ results[loop].code = finished_request->code;
+ os_free_memory(finished_request);
+ if (code == 0) {
+ loop++;
+ }
+ } else { /* finished_request is NULL */
+ /* pool is empty */
+ os_unlock_restore_context(shw_queue_lock, lock_context);
+ }
+
+ } while ((loop < result_size) && (finished_request != NULL));
+
+ *result_count = loop;
+
+ return FSL_RETURN_OK_S;
+}
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_get_capabilities);
+#endif
+fsl_shw_pco_t *fsl_shw_get_capabilities(fsl_shw_uco_t * user_ctx)
+{
+
+ /* Unused */
+ (void)user_ctx;
+
+ return &cap;
+}
+
+#if !(defined(FSL_HAVE_SAHARA) || defined(FSL_HAVE_RNGA) \
+ || defined(FSL_HAVE_RNGB) || defined(FSL_HAVE_RNGC))
+
+#if defined(LINUX_VERSION_CODE)
+EXPORT_SYMBOL(fsl_shw_get_random);
+#endif
+fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data)
+{
+
+ /* Unused */
+ (void)user_ctx;
+ (void)length;
+ (void)data;
+
+ return FSL_RETURN_ERROR_S;
+}
+
+#if defined(LINUX_VERSION_CODE)
+EXPORT_SYMBOL(fsl_shw_add_entropy);
+#endif
+fsl_shw_return_t fsl_shw_add_entropy(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data)
+{
+
+ /* Unused */
+ (void)user_ctx;
+ (void)length;
+ (void)data;
+
+ return FSL_RETURN_ERROR_S;
+}
+#endif
+
+#if !defined(FSL_HAVE_DRYICE) && !defined(FSL_HAVE_SAHARA2)
+#if 0
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_symmetric_decrypt);
+#endif
+fsl_shw_return_t fsl_shw_symmetric_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * ct, uint8_t * pt)
+{
+
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)sym_ctx;
+ (void)length;
+ (void)ct;
+ (void)pt;
+
+ return FSL_RETURN_ERROR_S;
+}
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_symmetric_encrypt);
+#endif
+fsl_shw_return_t fsl_shw_symmetric_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * pt, uint8_t * ct)
+{
+
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)sym_ctx;
+ (void)length;
+ (void)pt;
+ (void)ct;
+
+ return FSL_RETURN_ERROR_S;
+}
+
+/* DryIce support provided in separate file */
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_establish_key);
+#endif
+fsl_shw_return_t fsl_shw_establish_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_key_wrap_t establish_type,
+ const uint8_t * key)
+{
+
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)establish_type;
+ (void)key;
+
+ return FSL_RETURN_ERROR_S;
+}
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_extract_key);
+#endif
+fsl_shw_return_t fsl_shw_extract_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ uint8_t * covered_key)
+{
+
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)covered_key;
+
+ return FSL_RETURN_ERROR_S;
+}
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_release_key);
+#endif
+fsl_shw_return_t fsl_shw_release_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info)
+{
+
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+
+ return FSL_RETURN_ERROR_S;
+}
+#endif
+#endif /* SAHARA or DRYICE */
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_hash);
+#endif
+#if !defined(FSL_HAVE_SAHARA)
+fsl_shw_return_t fsl_shw_hash(fsl_shw_uco_t * user_ctx,
+ fsl_shw_hco_t * hash_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ /* Unused */
+ (void)user_ctx;
+ (void)hash_ctx;
+ (void)msg;
+ (void)length;
+ (void)result;
+ (void)result_len;
+
+ return ret;
+}
+#endif
+
+#ifndef FSL_HAVE_SAHARA
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_hmac_precompute);
+#endif
+
+fsl_shw_return_t fsl_shw_hmac_precompute(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx)
+{
+ fsl_shw_return_t status = FSL_RETURN_ERROR_S;
+
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)hmac_ctx;
+
+ return status;
+}
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_hmac);
+#endif
+
+fsl_shw_return_t fsl_shw_hmac(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len)
+{
+ fsl_shw_return_t status = FSL_RETURN_ERROR_S;
+
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)hmac_ctx;
+ (void)msg;
+ (void)length;
+ (void)result;
+ (void)result_len;
+
+ return status;
+}
+#endif
+
+/*!
+ * Call the proper function to encrypt a region of encrypted secure memory
+ *
+ * @brief
+ *
+ * @param user_ctx User context of the partition owner (NULL in kernel)
+ * @param partition_base Base address (physical) of the partition
+ * @param offset_bytes Offset from base address of the data to be encrypted
+ * @param byte_count Length of the message (bytes)
+ * @param black_data Pointer to where the encrypted data is stored
+ * @param IV IV to use for encryption
+ * @param cypher_mode Cyphering mode to use, specified by type
+ * #fsl_shw_cypher_mode_t
+ *
+ * @return status
+ */
+fsl_shw_return_t
+do_scc_encrypt_region(fsl_shw_uco_t * user_ctx,
+ void *partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, uint8_t * black_data,
+ uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode)
+{
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+#ifdef FSL_HAVE_SCC2
+
+ scc_return_t scc_ret;
+
+#ifdef SHW_DEBUG
+ uint32_t *owner_32 = (uint32_t *) & (owner_id);
+
+ LOG_KDIAG_ARGS
+ ("partition base: %p, offset: %i, count: %i, black data: %p\n",
+ partition_base, offset_bytes, byte_count, (void *)black_data);
+
+ LOG_KDIAG_ARGS("Owner ID: %08x%08x\n", owner_32[1], owner_32[0]);
+#endif /* SHW_DEBUG */
+ (void)user_ctx;
+
+ os_cache_flush_range(black_data, byte_count);
+
+ scc_ret =
+ scc_encrypt_region((uint32_t) partition_base, offset_bytes,
+ byte_count, __virt_to_phys(black_data), IV,
+ cypher_mode);
+
+ if (scc_ret == SCC_RET_OK) {
+ retval = FSL_RETURN_OK_S;
+ } else {
+ retval = FSL_RETURN_ERROR_S;
+ }
+
+ /* The SCC2 DMA engine should have written to the black ram, so we need to
+ * invalidate that region of memory. Note that the red ram is not an
+ * because it is mapped with the cache disabled.
+ */
+ os_cache_inv_range(black_data, byte_count);
+
+#endif /* FSL_HAVE_SCC2 */
+ return retval;
+}
+
+/*!
+ * Call the proper function to decrypt a region of encrypted secure memory
+ *
+ * @brief
+ *
+ * @param user_ctx User context of the partition owner (NULL in kernel)
+ * @param partition_base Base address (physical) of the partition
+ * @param offset_bytes Offset from base address that the decrypted data
+ * shall be placed
+ * @param byte_count Length of the message (bytes)
+ * @param black_data Pointer to where the encrypted data is stored
+ * @param IV IV to use for decryption
+ * @param cypher_mode Cyphering mode to use, specified by type
+ * #fsl_shw_cypher_mode_t
+ *
+ * @return status
+ */
+fsl_shw_return_t
+do_scc_decrypt_region(fsl_shw_uco_t * user_ctx,
+ void *partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, const uint8_t * black_data,
+ uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode)
+{
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+
+#ifdef FSL_HAVE_SCC2
+
+ scc_return_t scc_ret;
+
+#ifdef SHW_DEBUG
+ uint32_t *owner_32 = (uint32_t *) & (owner_id);
+
+ LOG_KDIAG_ARGS
+ ("partition base: %p, offset: %i, count: %i, black data: %p\n",
+ partition_base, offset_bytes, byte_count, (void *)black_data);
+
+ LOG_KDIAG_ARGS("Owner ID: %08x%08x\n", owner_32[1], owner_32[0]);
+#endif /* SHW_DEBUG */
+
+ (void)user_ctx;
+
+ /* The SCC2 DMA engine will be reading from the black ram, so we need to
+ * make sure that the data is pushed out of the cache. Note that the red
+ * ram is not an issue because it is mapped with the cache disabled.
+ */
+ os_cache_flush_range(black_data, byte_count);
+
+ scc_ret =
+ scc_decrypt_region((uint32_t) partition_base, offset_bytes,
+ byte_count,
+ (uint8_t *) __virt_to_phys(black_data), IV,
+ cypher_mode);
+
+ if (scc_ret == SCC_RET_OK) {
+ retval = FSL_RETURN_OK_S;
+ } else {
+ retval = FSL_RETURN_ERROR_S;
+ }
+
+#endif /* FSL_HAVE_SCC2 */
+
+ return retval;
+}
+
+void *fsl_shw_smalloc(fsl_shw_uco_t * user_ctx,
+ uint32_t size, const uint8_t * UMID, uint32_t permissions)
+{
+#ifdef FSL_HAVE_SCC2
+ int part_no;
+ void *part_base;
+ uint32_t part_phys;
+ scc_config_t *scc_configuration;
+
+ /* Check that the memory size requested is correct */
+ scc_configuration = scc_get_configuration();
+ if (size != scc_configuration->partition_size_bytes) {
+ return NULL;
+ }
+
+ /* attempt to grab a partition. */
+ if (scc_allocate_partition(0, &part_no, &part_base, &part_phys)
+ != SCC_RET_OK) {
+ return NULL;
+ }
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("Partition_base: %p, partition_base_phys: %p\n",
+ part_base, (void *)part_phys);
+#endif
+
+ if (scc_engage_partition(part_base, UMID, permissions)
+ != SCC_RET_OK) {
+ /* Engagement failed, so the partition needs to be de-allocated */
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("Failed to engage partition %p, de-allocating",
+ part_base);
+#endif
+ scc_release_partition(part_base);
+
+ return NULL;
+ }
+
+ return part_base;
+
+#else /* FSL_HAVE_SCC2 */
+
+ (void)user_ctx;
+ (void)size;
+ (void)UMID;
+ (void)permissions;
+ return NULL;
+
+#endif /* FSL_HAVE_SCC2 */
+}
+
+/* Release a block of secure memory */
+fsl_shw_return_t fsl_shw_sfree(fsl_shw_uco_t * user_ctx, void *address)
+{
+ (void)user_ctx;
+
+#ifdef FSL_HAVE_SCC2
+ if (scc_release_partition(address) == SCC_RET_OK) {
+ return FSL_RETURN_OK_S;
+ }
+#endif
+
+ return FSL_RETURN_ERROR_S;
+}
+
+/* Check the status of a block of secure memory */
+fsl_shw_return_t fsl_shw_sstatus(fsl_shw_uco_t * user_ctx,
+ void *address,
+ fsl_shw_partition_status_t * part_status)
+{
+ (void)user_ctx;
+
+#ifdef FSL_HAVE_SCC2
+ *part_status = scc_partition_status(address);
+
+ return FSL_RETURN_OK_S;
+#endif
+
+ return FSL_RETURN_ERROR_S;
+}
+
+/* Diminish permissions on some secure memory */
+fsl_shw_return_t fsl_shw_diminish_perms(fsl_shw_uco_t * user_ctx,
+ void *address, uint32_t permissions)
+{
+
+ (void)user_ctx; /* unused parameter warning */
+
+#ifdef FSL_HAVE_SCC2
+ if (scc_diminish_permissions(address, permissions) == SCC_RET_OK) {
+ return FSL_RETURN_OK_S;
+ }
+#endif
+ return FSL_RETURN_ERROR_S;
+}
+
+#ifndef FSL_HAVE_SAHARA
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_gen_encrypt);
+#endif
+
+fsl_shw_return_t fsl_shw_gen_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * cipher_key_info,
+ fsl_shw_sko_t * auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t * auth_data,
+ uint32_t payload_length,
+ const uint8_t * payload,
+ uint8_t * ct, uint8_t * auth_value)
+{
+ volatile fsl_shw_return_t status = FSL_RETURN_ERROR_S;
+
+ /* Unused */
+ (void)user_ctx;
+ (void)auth_ctx;
+ (void)cipher_key_info;
+ (void)auth_key_info; /* save compilation warning */
+ (void)auth_data_length;
+ (void)auth_data;
+ (void)payload_length;
+ (void)payload;
+ (void)ct;
+ (void)auth_value;
+
+ return status;
+}
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_auth_decrypt);
+#endif
+/*!
+ * @brief Authenticate and decrypt a (CCM) stream.
+ *
+ * @param user_ctx The user's context
+ * @param auth_ctx Info on this Auth operation
+ * @param cipher_key_info Key to encrypt payload
+ * @param auth_key_info (unused - same key in CCM)
+ * @param auth_data_length Length in bytes of @a auth_data
+ * @param auth_data Any auth-only data
+ * @param payload_length Length in bytes of @a payload
+ * @param ct The encrypted data
+ * @param auth_value The authentication code to validate
+ * @param[out] payload The location to store decrypted data
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_auth_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * cipher_key_info,
+ fsl_shw_sko_t * auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t * auth_data,
+ uint32_t payload_length,
+ const uint8_t * ct,
+ const uint8_t * auth_value,
+ uint8_t * payload)
+{
+ volatile fsl_shw_return_t status = FSL_RETURN_ERROR_S;
+
+ /* Unused */
+ (void)user_ctx;
+ (void)auth_ctx;
+ (void)cipher_key_info;
+ (void)auth_key_info; /* save compilation warning */
+ (void)auth_data_length;
+ (void)auth_data;
+ (void)payload_length;
+ (void)ct;
+ (void)auth_value;
+ (void)payload;
+
+ return status;
+}
+
+#endif /* no SAHARA */
+
+#ifndef FSL_HAVE_DRYICE
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_gen_random_pf_key);
+#endif
+/*!
+ * Cause the hardware to create a new random key for secure memory use.
+ *
+ * Have the hardware use the secure hardware random number generator to load a
+ * new secret key into the hardware random key register.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_gen_random_pf_key(fsl_shw_uco_t * user_ctx)
+{
+ volatile fsl_shw_return_t status = FSL_RETURN_ERROR_S;
+
+ return status;
+}
+
+#endif /* not have DRYICE */
+
+fsl_shw_return_t alloc_slot(fsl_shw_uco_t * user_ctx, fsl_shw_sko_t * key_info)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_alloc(user_ctx,
+ key_info->key_length,
+ key_info->userid,
+ &(key_info->handle));
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("key length: %i, handle: %i",
+ key_info->key_length, key_info->handle);
+#endif
+
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_alloc(key_info->keystore,
+ key_info->key_length,
+ key_info->userid,
+ &(key_info->handle));
+ }
+
+ return ret;
+} /* end fn alloc_slot */
+
+fsl_shw_return_t load_slot(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info, const uint8_t * key)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_load(user_ctx,
+ key_info->userid,
+ key_info->handle, key,
+ key_info->key_length);
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_load(key_info->keystore,
+ key_info->userid,
+ key_info->handle, key,
+ key_info->key_length);
+ }
+
+ return ret;
+} /* end fn load_slot */
+
+fsl_shw_return_t dealloc_slot(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ do_system_keystore_slot_dealloc(user_ctx,
+ key_info->userid,
+ key_info->handle);
+ } else {
+ /* Key goes in user keystore */
+ keystore_slot_dealloc(key_info->keystore,
+ key_info->userid, key_info->handle);
+ }
+
+ key_info->flags &= ~(FSL_SKO_KEY_ESTABLISHED | FSL_SKO_KEY_PRESENT);
+
+ return ret;
+} /* end fn slot_dealloc */
diff --git a/drivers/mxc/security/rng/shw_dryice.c b/drivers/mxc/security/rng/shw_dryice.c
new file mode 100644
index 000000000000..bcf1794f7351
--- /dev/null
+++ b/drivers/mxc/security/rng/shw_dryice.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include "shw_driver.h"
+#include "../dryice.h"
+
+#include <diagnostic.h>
+
+#ifdef FSL_HAVE_DRYICE
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_gen_random_pf_key);
+#endif
+/*!
+ * Cause the hardware to create a new random key for secure memory use.
+ *
+ * Have the hardware use the secure hardware random number generator to load a
+ * new secret key into the hardware random key register.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_gen_random_pf_key(fsl_shw_uco_t * user_ctx)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+ di_return_t di_ret;
+
+ /* For now, only blocking mode calls are supported */
+ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ di_ret = dryice_set_random_key(0);
+ if (di_ret != DI_SUCCESS) {
+ printk("dryice_set_random_key returned %d\n", di_ret);
+ goto out;
+ }
+
+ ret = FSL_RETURN_OK_S;
+
+ out:
+ return ret;
+}
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_read_tamper_event);
+#endif
+fsl_shw_return_t fsl_shw_read_tamper_event(fsl_shw_uco_t * user_ctx,
+ fsl_shw_tamper_t * tamperp,
+ uint64_t * timestampp)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+ di_return_t di_ret;
+ uint32_t di_events = 0;
+ uint32_t di_time_stamp;
+
+ /* Only blocking mode calls are supported */
+ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ di_ret = dryice_get_tamper_event(&di_events, &di_time_stamp, 0);
+ if ((di_ret != DI_SUCCESS) && (di_ret != DI_ERR_STATE)) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("dryice_get_tamper_event returned %s\n",
+ di_error_string(di_ret));
+#endif
+ goto out;
+ }
+
+ /* Pass time back to caller */
+ *timestampp = (uint64_t) di_time_stamp;
+
+ if (di_events & DI_TAMPER_EVENT_WTD) {
+ *tamperp = FSL_SHW_TAMPER_WTD;
+ } else if (di_events & DI_TAMPER_EVENT_ETBD) {
+ *tamperp = FSL_SHW_TAMPER_ETBD;
+ } else if (di_events & DI_TAMPER_EVENT_ETAD) {
+ *tamperp = FSL_SHW_TAMPER_ETAD;
+ } else if (di_events & DI_TAMPER_EVENT_EBD) {
+ *tamperp = FSL_SHW_TAMPER_EBD;
+ } else if (di_events & DI_TAMPER_EVENT_SAD) {
+ *tamperp = FSL_SHW_TAMPER_SAD;
+ } else if (di_events & DI_TAMPER_EVENT_TTD) {
+ *tamperp = FSL_SHW_TAMPER_TTD;
+ } else if (di_events & DI_TAMPER_EVENT_CTD) {
+ *tamperp = FSL_SHW_TAMPER_CTD;
+ } else if (di_events & DI_TAMPER_EVENT_VTD) {
+ *tamperp = FSL_SHW_TAMPER_VTD;
+ } else if (di_events & DI_TAMPER_EVENT_MCO) {
+ *tamperp = FSL_SHW_TAMPER_MCO;
+ } else if (di_events & DI_TAMPER_EVENT_TCO) {
+ *tamperp = FSL_SHW_TAMPER_TCO;
+ } else if (di_events != 0) {
+ /* Apparentliy a tamper type not known to this driver was detected */
+ goto out;
+ } else {
+ *tamperp = FSL_SHW_TAMPER_NONE;
+ }
+
+ ret = FSL_RETURN_OK_S;
+
+ out:
+ return ret;
+} /* end fn fsl_shw_read_tamper_event */
+#endif
+/*!
+ * Convert an SHW HW key reference into a DI driver key reference
+ *
+ * @param shw_pf_key An SHW HW key value
+ * @param di_keyp Location to store the equivalent DI driver key
+ *
+ * @return FSL_RETURN_OK_S, or error if key is unknown or cannot translate.
+ */
+fsl_shw_return_t shw_convert_pf_key(fsl_shw_pf_key_t shw_pf_key,
+ di_key_t * di_keyp)
+{
+ fsl_shw_return_t ret = FSL_RETURN_BAD_FLAG_S;
+
+ switch (shw_pf_key) {
+ case FSL_SHW_PF_KEY_IIM:
+ *di_keyp = DI_KEY_FK;
+ break;
+ case FSL_SHW_PF_KEY_RND:
+ *di_keyp = DI_KEY_RK;
+ break;
+ case FSL_SHW_PF_KEY_IIM_RND:
+ *di_keyp = DI_KEY_FRK;
+ break;
+ case FSL_SHW_PF_KEY_PRG:
+ *di_keyp = DI_KEY_PK;
+ break;
+ case FSL_SHW_PF_KEY_IIM_PRG:
+ *di_keyp = DI_KEY_FPK;
+ break;
+ default:
+ goto out;
+ }
+
+ ret = FSL_RETURN_OK_S;
+
+ out:
+ return ret;
+}
+
+#ifdef DIAG_SECURITY_FUNC
+const char *di_error_string(int code)
+{
+ char *str = "unknown";
+
+ switch (code) {
+ case DI_SUCCESS:
+ str = "operation was successful";
+ break;
+ case DI_ERR_BUSY:
+ str = "device or resource busy";
+ break;
+ case DI_ERR_STATE:
+ str = "dryice is in incompatible state";
+ break;
+ case DI_ERR_INUSE:
+ str = "resource is already in use";
+ break;
+ case DI_ERR_UNSET:
+ str = "resource has not been initialized";
+ break;
+ case DI_ERR_WRITE:
+ str = "error occurred during register write";
+ break;
+ case DI_ERR_INVAL:
+ str = "invalid argument";
+ break;
+ case DI_ERR_FAIL:
+ str = "operation failed";
+ break;
+ case DI_ERR_HLOCK:
+ str = "resource is hard locked";
+ break;
+ case DI_ERR_SLOCK:
+ str = "resource is soft locked";
+ break;
+ case DI_ERR_NOMEM:
+ str = "out of memory";
+ break;
+ default:
+ break;
+ }
+
+ return str;
+}
+#endif /* HAVE DRYICE */
diff --git a/drivers/mxc/security/rng/shw_hash.c b/drivers/mxc/security/rng/shw_hash.c
new file mode 100644
index 000000000000..3aac05235bfa
--- /dev/null
+++ b/drivers/mxc/security/rng/shw_hash.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file shw_hash.c
+ *
+ * This file contains implementations for use of the (internal) SHW hash
+ * software computation. It defines the usual three steps:
+ *
+ * - #shw_hash_init()
+ * - #shw_hash_update()
+ * - #shw_hash_final()
+ *
+ * In support of the above functions, it also contains these functions:
+ * - #sha256_init()
+ * - #sha256_process_block()
+ *
+ *
+ * These functions depend upon the Linux Endian functions __be32_to_cpu(),
+ * __cpu_to_be32() to convert a 4-byte big-endian array to an integer and
+ * vice-versa. For those without Linux, it should be pretty obvious what they
+ * do.
+ *
+ * The #shw_hash_update() and #shw_hash_final() functions are generic enough to
+ * support SHA-1/SHA-224/SHA-256, as needed. Some extra tweaking would be
+ * necessary to get them to support SHA-384/SHA-512.
+ *
+ */
+
+#include "shw_driver.h"
+#include "shw_hash.h"
+
+#ifndef __KERNEL__
+#include <asm/types.h>
+#include <linux/byteorder/little_endian.h> /* or whichever is proper for target arch */
+#define printk printf
+#endif
+
+/*!
+ * Rotate a value right by a number of bits.
+ *
+ * @param x Word of data which needs rotating
+ * @param y Number of bits to rotate
+ *
+ * @return The new value
+ */
+inline uint32_t rotr32fixed(uint32_t x, unsigned int y)
+{
+ return (uint32_t) ((x >> y) | (x << (32 - y)));
+}
+
+#define blk0(i) (W[i] = data[i])
+// Referencing parameters so many times is really poor practice. Do not imitate these macros
+#define blk2(i) (W[i & 15] += s1(W[(i - 2) & 15]) + W[(i - 7) & 15] + s0(W[(i - 15) & 15]))
+
+#define Ch(x,y,z) (z ^ (x & (y ^ z)))
+#define Maj(x,y,z) ((x & y) | (z & (x | y)))
+
+#define a(i) T[(0 - i) & 7]
+#define b(i) T[(1 - i) & 7]
+#define c(i) T[(2 - i) & 7]
+#define d(i) T[(3 - i) & 7]
+#define e(i) T[(4 - i) & 7]
+#define f(i) T[(5 - i) & 7]
+#define g(i) T[(6 - i) & 7]
+#define h(i) T[(7 - i) & 7]
+
+// This is a bad way to write a multi-statement macro... and referencing 'i' so many
+// times is really poor practice. Do not imitate.
+#define R(i) h(i) += S1( e(i)) + Ch(e(i), f(i), g(i)) + K[i + j] +(j ? blk2(i) : blk0(i));\
+ d(i) += h(i);h(i) += S0(a(i)) + Maj(a(i), b(i), c(i))
+
+// for SHA256
+#define S0(x) (rotr32fixed(x, 2) ^ rotr32fixed(x, 13) ^ rotr32fixed(x, 22))
+#define S1(x) (rotr32fixed(x, 6) ^ rotr32fixed(x, 11) ^ rotr32fixed(x, 25))
+#define s0(x) (rotr32fixed(x, 7) ^ rotr32fixed(x, 18) ^ (x >> 3))
+#define s1(x) (rotr32fixed(x, 17) ^ rotr32fixed(x, 19) ^ (x >> 10))
+
+/*!
+ * Initialize the Hash State
+ *
+ * Constructs the SHA256 hash engine.
+ * Specification:
+ * State Size = 32 bytes
+ * Block Size = 64 bytes
+ * Digest Size = 32 bytes
+ *
+ * @param state Address of hash state structure
+ *
+ */
+void sha256_init(shw_hash_state_t * state)
+{
+ state->bit_count = 0;
+ state->partial_count_bytes = 0;
+
+ state->state[0] = 0x6a09e667;
+ state->state[1] = 0xbb67ae85;
+ state->state[2] = 0x3c6ef372;
+ state->state[3] = 0xa54ff53a;
+ state->state[4] = 0x510e527f;
+ state->state[5] = 0x9b05688c;
+ state->state[6] = 0x1f83d9ab;
+ state->state[7] = 0x5be0cd19;
+}
+
+const uint32_t K[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+/*!
+ * Hash a block of data into the SHA-256 hash state.
+ *
+ * This function hash the block of data in the @c partial_block
+ * element of the state structure into the state variables of the
+ * state structure.
+ *
+ * @param state Address of hash state structure
+ *
+ */
+static void sha256_process_block(shw_hash_state_t * state)
+{
+ uint32_t W[16];
+ uint32_t T[8];
+ uint32_t stack_buffer[SHW_HASH_BLOCK_WORD_SIZE];
+ uint32_t *data = &stack_buffer[0];
+ uint8_t *input = state->partial_block;
+ unsigned int i;
+ unsigned int j;
+
+ /* Copy byte-oriented input block into word-oriented registers */
+ for (i = 0; i < SHW_HASH_BLOCK_LEN / sizeof(uint32_t);
+ i++, input += sizeof(uint32_t)) {
+ stack_buffer[i] = __be32_to_cpu(*(uint32_t *) input);
+ }
+
+ /* Copy context->state[] to working vars */
+ memcpy(T, state->state, sizeof(T));
+
+ /* 64 operations, partially loop unrolled */
+ for (j = 0; j < SHW_HASH_BLOCK_LEN; j += 16) {
+ R(0);
+ R(1);
+ R(2);
+ R(3);
+ R(4);
+ R(5);
+ R(6);
+ R(7);
+ R(8);
+ R(9);
+ R(10);
+ R(11);
+ R(12);
+ R(13);
+ R(14);
+ R(15);
+ }
+ /* Add the working vars back into context.state[] */
+ state->state[0] += a(0);
+ state->state[1] += b(0);
+ state->state[2] += c(0);
+ state->state[3] += d(0);
+ state->state[4] += e(0);
+ state->state[5] += f(0);
+ state->state[6] += g(0);
+ state->state[7] += h(0);
+
+ /* Wipe variables */
+ memset(W, 0, sizeof(W));
+ memset(T, 0, sizeof(T));
+}
+
+/*!
+ * Initialize the hash state structure
+ *
+ * @param state Address of hash state structure.
+ * @param alg Which hash algorithm to use (must be FSL_HASH_ALG_SHA1)
+ *
+ * @return FSL_RETURN_OK_S if all went well, otherwise an error code.
+ */
+fsl_shw_return_t shw_hash_init(shw_hash_state_t * state, fsl_shw_hash_alg_t alg)
+{
+ if (alg != FSL_HASH_ALG_SHA256) {
+ return FSL_RETURN_BAD_ALGORITHM_S;
+ }
+
+ sha256_init(state);
+
+ return FSL_RETURN_OK_S;
+}
+
+/*!
+ * Add input bytes to the hash
+ *
+ * The bytes are added to the partial_block element of the hash state, and as
+ * the partial block is filled, it is processed by sha1_process_block(). This
+ * function also updates the bit_count element of the hash state.
+ *
+ * @param state Address of hash state structure
+ * @param input Address of bytes to add to the hash
+ * @param input_len Numbef of bytes at @c input
+ *
+ */
+fsl_shw_return_t shw_hash_update(shw_hash_state_t * state,
+ const uint8_t * input, unsigned int input_len)
+{
+ unsigned int bytes_needed; /* Needed to fill a block */
+ unsigned int bytes_to_copy; /* to copy into the block */
+
+ /* Account for new data */
+ state->bit_count += 8 * input_len;
+
+ /*
+ * Process input bytes into the ongoing block; process the block when it
+ * gets full.
+ */
+ while (input_len > 0) {
+ bytes_needed = SHW_HASH_BLOCK_LEN - state->partial_count_bytes;
+ bytes_to_copy = ((input_len < bytes_needed) ?
+ input_len : bytes_needed);
+
+ /* Add in the bytes and do the accounting */
+ memcpy(state->partial_block + state->partial_count_bytes,
+ input, bytes_to_copy);
+ input += bytes_to_copy;
+ input_len -= bytes_to_copy;
+ state->partial_count_bytes += bytes_to_copy;
+
+ /* Run a full block through the transform */
+ if (state->partial_count_bytes == SHW_HASH_BLOCK_LEN) {
+ sha256_process_block(state);
+ state->partial_count_bytes = 0;
+ }
+ }
+
+ return FSL_RETURN_OK_S;
+} /* end fn shw_hash_update */
+
+/*!
+ * Finalize the hash
+ *
+ * Performs the finalize operation on the previous input data & returns the
+ * resulting digest. The finalize operation performs the appropriate padding
+ * up to the block size.
+ *
+ * @param state Address of hash state structure
+ * @param result Location to store the hash result
+ * @param result_len Number of bytes of @c result to be stored.
+ *
+ * @return FSL_RETURN_OK_S if all went well, FSL_RETURN_BAD_DATA_LENGTH_S if
+ * hash_len is too long, otherwise an error code.
+ */
+fsl_shw_return_t shw_hash_final(shw_hash_state_t * state, uint8_t * result,
+ unsigned int result_len)
+{
+ static const uint8_t pad[SHW_HASH_BLOCK_LEN * 2] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ uint8_t data[sizeof(state->bit_count)];
+ uint32_t pad_length;
+ uint64_t bit_count = state->bit_count;
+ uint8_t hash[SHW_HASH_LEN];
+ int i;
+
+ if (result_len > SHW_HASH_LEN) {
+ return FSL_RETURN_BAD_DATA_LENGTH_S;
+ }
+
+ /* Save the length before padding. */
+ for (i = sizeof(state->bit_count) - 1; i >= 0; i--) {
+ data[i] = bit_count & 0xFF;
+ bit_count >>= 8;
+ }
+ pad_length = ((state->partial_count_bytes < 56) ?
+ (56 - state->partial_count_bytes) :
+ (120 - state->partial_count_bytes));
+
+ /* Pad to 56 bytes mod 64 (BLOCK_SIZE). */
+ shw_hash_update(state, pad, pad_length);
+
+ /*
+ * Append the length. This should trigger transform of the final block.
+ */
+ shw_hash_update(state, data, sizeof(state->bit_count));
+
+ /* Copy the result into a byte array */
+ for (i = 0; i < SHW_HASH_STATE_WORDS; i++) {
+ *(uint32_t *) (hash + 4 * i) = __cpu_to_be32(state->state[i]);
+ }
+
+ /* And copy the result out to caller */
+ memcpy(result, hash, result_len);
+
+ return FSL_RETURN_OK_S;
+} /* end fn shw_hash_final */
diff --git a/drivers/mxc/security/rng/shw_hmac.c b/drivers/mxc/security/rng/shw_hmac.c
new file mode 100644
index 000000000000..57c086405a49
--- /dev/null
+++ b/drivers/mxc/security/rng/shw_hmac.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file shw_hmac.c
+ *
+ * This file contains implementations for use of the (internal) SHW HMAC
+ * software computation. It defines the usual three steps:
+ *
+ * - #shw_hmac_init()
+ * - #shw_hmac_update()
+ * - #shw_hmac_final()
+ *
+ *
+ */
+
+#include "shw_driver.h"
+#include "shw_hmac.h"
+
+#ifndef __KERNEL__
+#include <asm/types.h>
+#include <linux/byteorder/little_endian.h> /* or whichever is proper for target arch */
+#define printk printf
+#endif
+
+/*! XOR value for HMAC inner key */
+#define INNER_HASH_CONSTANT 0x36
+
+/*! XOR value for HMAC outer key */
+#define OUTER_HASH_CONSTANT 0x5C
+
+/*!
+ * Initialize the HMAC state structure with the HMAC key
+ *
+ * @param state Address of HMAC state structure
+ * @param key Address of the key to be used for the HMAC.
+ * @param key_len Number of bytes of @c key.
+ *
+ * Convert the key into its equivalent inner and outer hash state objects.
+ *
+ * @return FSL_RETURN_OK_S if all went well, otherwise an error code.
+ */
+fsl_shw_return_t shw_hmac_init(shw_hmac_state_t * state,
+ const uint8_t * key, unsigned int key_len)
+{
+ fsl_shw_return_t code = FSL_RETURN_ERROR_S;
+ uint8_t first_block[SHW_HASH_BLOCK_LEN];
+ unsigned int i;
+
+ /* Don't bother handling the pre-hash. */
+ if (key_len > SHW_HASH_BLOCK_LEN) {
+ code = FSL_RETURN_BAD_KEY_LENGTH_S;
+ goto out;
+ }
+
+ /* Prepare inner hash */
+ for (i = 0; i < SHW_HASH_BLOCK_LEN; i++) {
+ if (i < key_len) {
+ first_block[i] = key[i] ^ INNER_HASH_CONSTANT;
+ } else {
+ first_block[i] = INNER_HASH_CONSTANT;
+ }
+ }
+ code = shw_hash_init(&state->inner_hash, FSL_HASH_ALG_SHA256);
+ if (code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ shw_hash_update(&state->inner_hash, first_block, SHW_HASH_BLOCK_LEN);
+
+ /* Prepare outer hash */
+ for (i = 0; i < SHW_HASH_BLOCK_LEN; i++) {
+ if (i < key_len) {
+ first_block[i] = key[i] ^ OUTER_HASH_CONSTANT;
+ } else {
+ first_block[i] = OUTER_HASH_CONSTANT;
+ }
+ }
+ code = shw_hash_init(&state->outer_hash, FSL_HASH_ALG_SHA256);
+ if (code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ shw_hash_update(&state->outer_hash, first_block, SHW_HASH_BLOCK_LEN);
+
+ /* Wipe evidence of key */
+ memset(first_block, 0, SHW_HASH_BLOCK_LEN);
+
+ out:
+ return code;
+}
+
+/*!
+ * Put data into the HMAC calculation
+ *
+ * Send the msg data inner inner hash's update function.
+ *
+ * @param state Address of HMAC state structure.
+ * @param msg Address of the message data for the HMAC.
+ * @param msg_len Number of bytes of @c msg.
+ *
+ * @return FSL_RETURN_OK_S if all went well, otherwise an error code.
+ */
+fsl_shw_return_t shw_hmac_update(shw_hmac_state_t * state,
+ const uint8_t * msg, unsigned int msg_len)
+{
+ shw_hash_update(&state->inner_hash, msg, msg_len);
+
+ return FSL_RETURN_OK_S;
+}
+
+/*!
+ * Calculate the final HMAC
+ *
+ * @param state Address of HMAC state structure.
+ * @param hmac Address of location to store the HMAC.
+ * @param hmac_len Number of bytes of @c mac to be stored. Probably best if
+ * this value is no greater than #SHW_HASH_LEN.
+ *
+ * This function finalizes the internal hash, and uses that result as
+ * data for the outer hash. As many bytes of that result are passed
+ * to the user as desired.
+ *
+ * @return FSL_RETURN_OK_S if all went well, otherwise an error code.
+ */
+fsl_shw_return_t shw_hmac_final(shw_hmac_state_t * state,
+ uint8_t * hmac, unsigned int hmac_len)
+{
+ uint8_t hash_result[SHW_HASH_LEN];
+
+ shw_hash_final(&state->inner_hash, hash_result, sizeof(hash_result));
+ shw_hash_update(&state->outer_hash, hash_result, SHW_HASH_LEN);
+
+ shw_hash_final(&state->outer_hash, hmac, hmac_len);
+
+ return FSL_RETURN_OK_S;
+}
diff --git a/drivers/mxc/security/rng/shw_memory_mapper.c b/drivers/mxc/security/rng/shw_memory_mapper.c
new file mode 100644
index 000000000000..d7b82576a6cd
--- /dev/null
+++ b/drivers/mxc/security/rng/shw_memory_mapper.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+
+
+/**
+ * Memory management functions, from Sahara Crypto API
+ *
+ * This is a subset of the memory management functions from the Sahara Crypto
+ * API, and is intended to support user secure partitions.
+ */
+
+#include "portable_os.h"
+#include "fsl_shw.h"
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+
+#ifdef SHW_DEBUG
+#include <diagnostic.h>
+#endif
+
+/* Page context structure. Used by wire_user_memory and unwire_user_memory */
+typedef struct page_ctx_t {
+ uint32_t count;
+ struct page **local_pages;
+} page_ctx_t;
+
+/**
+*******************************************************************************
+* Map and wire down a region of user memory.
+*
+*
+* @param address Userspace address of the memory to wire
+* @param length Length of the memory region to wire
+* @param page_ctx Page context, to be passed to unwire_user_memory
+*
+* @return (if successful) Kernel virtual address of the wired pages
+*/
+void* wire_user_memory(void* address, uint32_t length, void **page_ctx)
+{
+ void* kernel_black_addr = NULL;
+ int result = -1;
+ int page_index = 0;
+ page_ctx_t *page_context;
+ int nr_pages = 0;
+ unsigned long start_page;
+ fsl_shw_return_t status;
+
+ /* Determine the number of pages being used for this link */
+ nr_pages = (((unsigned long)(address) & ~PAGE_MASK)
+ + length + ~PAGE_MASK) >> PAGE_SHIFT;
+
+ start_page = (unsigned long)(address) & PAGE_MASK;
+
+ /* Allocate some memory to keep track of the wired user pages, so that
+ * they can be deallocated later. The block of memory will contain both
+ * the structure and the array of pages.
+ */
+ page_context = kmalloc(sizeof(page_ctx_t)
+ + nr_pages * sizeof(struct page *), GFP_KERNEL);
+
+ if (page_context == NULL) {
+ status = FSL_RETURN_NO_RESOURCE_S; /* no memory! */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("kmalloc() failed.");
+#endif
+ return NULL;
+ }
+
+ /* Set the page pointer to point to the allocated region of memory */
+ page_context->local_pages = (void*)page_context + sizeof(page_ctx_t);
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS("page_context at: %p, local_pages at: %p",
+ (void *)page_context,
+ (void *)(page_context->local_pages));
+#endif
+
+ /* Wire down the pages from user space */
+ down_read(&current->mm->mmap_sem);
+ result = get_user_pages(current, current->mm,
+ start_page, nr_pages,
+ WRITE, 0 /* noforce */,
+ (page_context->local_pages), NULL);
+ up_read(&current->mm->mmap_sem);
+
+ if (result < nr_pages) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("get_user_pages() failed.");
+#endif
+ if (result > 0) {
+ for (page_index = 0; page_index < result; page_index++)
+ page_cache_release((page_context->local_pages[page_index]));
+
+ kfree(page_context);
+ }
+ return NULL;
+ }
+
+ kernel_black_addr = page_address(page_context->local_pages[0]) +
+ ((unsigned long)address & ~PAGE_MASK);
+
+ page_context->count = nr_pages;
+ *page_ctx = page_context;
+
+ return kernel_black_addr;
+}
+
+
+/**
+*******************************************************************************
+* Release and unmap a region of user memory.
+*
+* @param page_ctx Page context from wire_user_memory
+*/
+void unwire_user_memory(void** page_ctx)
+{
+ int page_index = 0;
+ struct page_ctx_t *page_context = *page_ctx;
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS("page_context at: %p, first page at:%p, count: %i",
+ (void *)page_context,
+ (void *)(page_context->local_pages),
+ page_context->count);
+#endif
+
+ if ((page_context != NULL) && (page_context->local_pages != NULL)) {
+ for (page_index = 0; page_index < page_context->count; page_index++)
+ page_cache_release(page_context->local_pages[page_index]);
+
+ kfree(page_context);
+ *page_ctx = NULL;
+ }
+}
+
+
+/**
+*******************************************************************************
+* Map some physical memory into a users memory space
+*
+* @param vma Memory structure to map to
+* @param physical_addr Physical address of the memory to be mapped in
+* @param size Size of the memory to map (bytes)
+*
+* @return
+*/
+os_error_code
+map_user_memory(struct vm_area_struct *vma, uint32_t physical_addr, uint32_t size)
+{
+ os_error_code retval;
+
+ /* Map the acquired partition into the user's memory space */
+ vma->vm_end = vma->vm_start + size;
+
+ /* set cache policy to uncached so that each write of the UMID and
+ * permissions get directly to the SCC2 in order to engage it
+ * properly. Once the permissions have been written, it may be
+ * useful to provide a service for the user to request a different
+ * cache policy
+ */
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ /* Make sure that the user cannot fork() a child which will inherit
+ * this mapping, as it creates a security hole. Likewise, do not
+ * allow the user to 'expand' his mapping beyond this partition.
+ */
+ vma->vm_flags |= VM_IO | VM_RESERVED | VM_DONTCOPY | VM_DONTEXPAND;
+
+ retval = remap_pfn_range(vma,
+ vma->vm_start,
+ __phys_to_pfn(physical_addr),
+ size,
+ vma->vm_page_prot);
+
+ return retval;
+}
+
+
+/**
+*******************************************************************************
+* Remove some memory from a user's memory space
+*
+* @param user_addr Userspace address of the memory to be unmapped
+* @param size Size of the memory to map (bytes)
+*
+* @return
+*/
+os_error_code
+unmap_user_memory(uint32_t user_addr, uint32_t size)
+{
+ os_error_code retval;
+ struct mm_struct *mm = current->mm;
+
+ /* Unmap the memory region (see sys_munmap in mmap.c) */
+ down_write(&mm->mmap_sem);
+ retval = do_munmap(mm, (unsigned long)user_addr, size);
+ up_write(&mm->mmap_sem);
+
+ return retval;
+}
diff --git a/drivers/mxc/security/sahara2/Kconfig b/drivers/mxc/security/sahara2/Kconfig
new file mode 100644
index 000000000000..ab4e6fdc2cfc
--- /dev/null
+++ b/drivers/mxc/security/sahara2/Kconfig
@@ -0,0 +1,35 @@
+menu "SAHARA2 Security Hardware Support"
+
+config MXC_SAHARA
+ tristate "Security Hardware Support (FSL SHW)"
+ ---help---
+ Provides driver and kernel mode API for using cryptographic
+ accelerators.
+
+config MXC_SAHARA_USER_MODE
+ tristate "User Mode API for FSL SHW"
+ depends on MXC_SAHARA
+ ---help---
+ Provides kernel driver for User Mode API.
+
+config MXC_SAHARA_POLL_MODE
+ bool "Force driver to POLL for hardware completion."
+ depends on MXC_SAHARA
+ default n
+ ---help---
+ When this flag is yes, the driver will not use interrupts to
+ determine when the hardware has completed a task, but instead
+ will hold onto the CPU and continually poll the hardware until
+ it completes.
+
+config MXC_SAHARA_POLL_MODE_TIMEOUT
+ hex "Poll loop timeout"
+ depends on MXC_SAHARA_POLL_MODE
+ default "0xFFFFFFFF"
+ help
+ To avoid infinite polling, a timeout is provided. Should the
+ timeout be reached, a fault is reported, indicating there must
+ be something wrong with SAHARA, and SAHARA is reset. The loop
+ will exit after the given number of iterations.
+
+endmenu
diff --git a/drivers/mxc/security/sahara2/Makefile b/drivers/mxc/security/sahara2/Makefile
new file mode 100644
index 000000000000..b515709be29b
--- /dev/null
+++ b/drivers/mxc/security/sahara2/Makefile
@@ -0,0 +1,47 @@
+# Makefile for the Linux Sahara2 driver
+#
+# This makefile works within a kernel driver tree
+
+# Need to augment this to support optionally building user-mode support
+API_SOURCES = fsl_shw_sym.c fsl_shw_user.c fsl_shw_hash.c fsl_shw_auth.c \
+ fsl_shw_hmac.c fsl_shw_rand.c sf_util.c km_adaptor.c fsl_shw_keystore.c \
+ fsl_shw_wrap.c \
+
+
+SOURCES = sah_driver_interface.c sah_hardware_interface.c \
+ sah_interrupt_handler.c sah_queue.c sah_queue_manager.c \
+ sah_status_manager.c sah_memory_mapper.c
+
+
+# Turn on for mostly full debugging
+# DIAGS = -DDIAG_DRV_STATUS -DDIAG_DRV_QUEUE -DDIAG_DRV_INTERRUPT -DDIAG_DRV_IF
+# DIAGS += -DDIAG_DURING_INTERRUPT
+
+# Turn on for lint-type checking
+#EXTRA_CFLAGS = -Wall -W -Wstrict-prototypes -Wmissing-prototypes
+EXTRA_CFLAGS += -DLINUX_KERNEL $(DIAGS)
+
+
+ifeq ($(CONFIG_MXC_SAHARA_POLL_MODE),y)
+EXTRA_CFLAGS += -DSAHARA_POLL_MODE
+EXTRA_CFLAGS += -DSAHARA_POLL_MODE_TIMEOUT=$(CONFIG_SAHARA_POLL_MODE_TIMEOUT)
+endif
+
+ifeq ($(CONFIG_MXC_SAHARA_USER_MODE),y)
+EXTRA_CFLAGS += -DSAHARA_USER_MODE
+SOURCES +=
+endif
+
+ifeq ($(CONFIG_PM),y)
+EXTRA_CFLAGS += -DSAHARA_POWER_MANAGMENT
+endif
+
+EXTRA_CFLAGS += -Idrivers/mxc/security/sahara2/include
+
+# handle buggy BSP -- uncomment if these are undefined during build
+#EXTRA_CFLAGS += -DSAHARA_BASE_ADDR=HAC_BASE_ADDR -DINT_SAHARA=INT_HAC_RTIC
+
+
+obj-$(CONFIG_MXC_SAHARA) += sahara.o
+
+sahara-objs := $(SOURCES:.c=.o) $(API_SOURCES:.c=.o)
diff --git a/drivers/mxc/security/sahara2/fsl_shw_auth.c b/drivers/mxc/security/sahara2/fsl_shw_auth.c
new file mode 100644
index 000000000000..161feac8e52b
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_auth.c
@@ -0,0 +1,706 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+/*!
+ * @file fsl_shw_auth.c
+ *
+ * This file contains the routines which do the combined encrypt+authentication
+ * functions. For now, only AES-CCM is supported.
+ */
+
+#include "sahara.h"
+#include "adaptor.h"
+#include "sf_util.h"
+
+#ifdef __KERNEL__
+EXPORT_SYMBOL(fsl_shw_gen_encrypt);
+EXPORT_SYMBOL(fsl_shw_auth_decrypt);
+#endif
+
+
+/*! Size of buffer to repetively sink useless CBC output */
+#define CBC_BUF_LEN 4096
+
+/*!
+ * Compute the size, in bytes, of the encoded auth length
+ *
+ * @param l The actual associated data length
+ *
+ * @return The encoded length
+ */
+#define COMPUTE_NIST_AUTH_LEN_SIZE(l) \
+({ \
+ unsigned val; \
+ uint32_t len = l; \
+ if (len == 0) { \
+ val = 0; \
+ } else if (len < 65280) { \
+ val = 2; \
+ } else { /* cannot handle >= 2^32 */ \
+ val = 6; \
+ } \
+ val; \
+})
+
+/*!
+ * Store the encoded Auth Length into the Auth Data
+ *
+ * @param l The actual Auth Length
+ * @param p Location to store encoding (must be uint8_t*)
+ *
+ * @return void
+ */
+#define STORE_NIST_AUTH_LEN(l, p) \
+{ \
+ register uint32_t L = l; \
+ if ((uint32_t)(l) < 65280) { \
+ (p)[1] = L & 0xff; \
+ L >>= 8; \
+ (p)[0] = L & 0xff; \
+ } else { /* cannot handle >= 2^32 */ \
+ int i; \
+ for (i = 5; i > 1; i--) { \
+ (p)[i] = L & 0xff; \
+ L >>= 8; \
+ } \
+ (p)[1] = 0xfe; /* Markers */ \
+ (p)[0] = 0xff; \
+ } \
+}
+
+#if defined (FSL_HAVE_SAHARA2) || defined (USE_S2_CCM_DECRYPT_CHAIN) \
+ || defined (USE_S2_CCM_ENCRYPT_CHAIN)
+/*! Buffer to repetively sink useless CBC output */
+static uint8_t cbc_buffer[CBC_BUF_LEN];
+#endif
+
+/*!
+ * Place to store useless output (while bumping CTR0 to CTR1, for instance.
+ * Must be maximum Symmetric block size
+ */
+static uint8_t garbage_output[16];
+
+/*!
+ * Block of zeroes which is maximum Symmetric block size, used for
+ * initializing context register, etc.
+ */
+static uint8_t block_zeros[16] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*!
+ * Append a descriptor chain which will compute CBC over the
+ * formatted associated data blocks.
+ *
+ * @param[in,out] link1 Where to append the new link
+ * @param[in,out] data_len Location of current/updated auth-only data length
+ * @param user_ctx Info for acquiring memory
+ * @param auth_ctx Location of block0 value
+ * @param auth_data Unformatted associated data
+ * @param auth_data_length Length in octets of @a auth_data
+ * @param[in,out] temp_buf Location of in-process data.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static inline fsl_shw_return_t process_assoc_from_nist_params(sah_Link ** link1,
+ uint32_t *
+ data_len,
+ fsl_shw_uco_t *
+ user_ctx,
+ fsl_shw_acco_t *
+ auth_ctx,
+ const uint8_t *
+ auth_data,
+ uint32_t
+ auth_data_length,
+ uint8_t **
+ temp_buf)
+{
+ fsl_shw_return_t status;
+ uint32_t auth_size_length =
+ COMPUTE_NIST_AUTH_LEN_SIZE(auth_data_length);
+ uint32_t auth_pad_length =
+ auth_ctx->auth_info.CCM_ctx_info.block_size_bytes -
+ (auth_data_length +
+ auth_size_length) %
+ auth_ctx->auth_info.CCM_ctx_info.block_size_bytes;
+
+ if (auth_pad_length ==
+ auth_ctx->auth_info.CCM_ctx_info.block_size_bytes) {
+ auth_pad_length = 0;
+ }
+
+ /* Put in Block0 */
+ status = sah_Create_Link(user_ctx->mem_util, link1,
+ auth_ctx->auth_info.CCM_ctx_info.context,
+ auth_ctx->auth_info.CCM_ctx_info.
+ block_size_bytes, SAH_USES_LINK_DATA);
+
+ if (auth_data_length != 0) {
+ if (status == FSL_RETURN_OK_S) {
+ /* Add on length preamble to auth data */
+ STORE_NIST_AUTH_LEN(auth_data_length, *temp_buf);
+ status = sah_Append_Link(user_ctx->mem_util, *link1,
+ *temp_buf, auth_size_length,
+ SAH_OWNS_LINK_DATA);
+ *temp_buf += auth_size_length; /* 2, 6, or 10 bytes */
+ }
+
+ if (status == FSL_RETURN_OK_S) {
+ /* Add in auth data */
+ status = sah_Append_Link(user_ctx->mem_util, *link1,
+ (uint8_t *) auth_data,
+ auth_data_length,
+ SAH_USES_LINK_DATA);
+ }
+
+ if ((status == FSL_RETURN_OK_S) && (auth_pad_length > 0)) {
+ status = sah_Append_Link(user_ctx->mem_util, *link1,
+ block_zeros, auth_pad_length,
+ SAH_USES_LINK_DATA);
+ }
+ }
+ /* ... if auth_data_length != 0 */
+ *data_len = auth_ctx->auth_info.CCM_ctx_info.block_size_bytes +
+ auth_data_length + auth_size_length + auth_pad_length;
+
+ return status;
+} /* end fn process_assoc_from_nist_params */
+
+/*!
+ * Add a Descriptor which will process with CBC the NIST preamble data
+ *
+ * @param desc_chain Current chain
+ * @param user_ctx User's context
+ * @param auth_ctx Inf
+ * @pararm encrypt 0 => decrypt, non-zero => encrypt
+ * @param auth_data Additional auth data for this call
+ * @param auth_data_length Length in bytes of @a auth_data
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static inline fsl_shw_return_t add_assoc_preamble(sah_Head_Desc ** desc_chain,
+ fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ int encrypt,
+ const uint8_t * auth_data,
+ uint32_t auth_data_length)
+{
+ uint8_t *temp_buf;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ uint32_t cbc_data_length = 0;
+ /* Assume AES */
+ uint32_t header = SAH_HDR_SKHA_ENC_DEC;
+ uint32_t temp_buf_flag;
+ unsigned chain_s2 = 1;
+
+#if defined (FSL_HAVE_SAHARA4) && !defined (USE_S2_CCM_DECRYPT_CHAIN)
+ if (!encrypt) {
+ chain_s2 = 0;
+ }
+#endif
+#if defined (FSL_HAVE_SAHARA4) && !defined (USE_S2_CCM_ENCRYPT_CHAIN)
+ if (encrypt) {
+ chain_s2 = 0;
+ }
+#endif
+ /* Grab a block big enough for multiple uses so that only one allocate
+ * request needs to be made.
+ */
+ temp_buf =
+ user_ctx->mem_util->mu_malloc(user_ctx->mem_util->mu_ref,
+ 3 *
+ auth_ctx->auth_info.CCM_ctx_info.
+ block_size_bytes);
+
+ if (temp_buf == NULL) {
+ status = FSL_RETURN_NO_RESOURCE_S;
+ goto out;
+ }
+
+ if (auth_ctx->flags & FSL_ACCO_NIST_CCM) {
+ status = process_assoc_from_nist_params(&link1,
+ &cbc_data_length,
+ user_ctx,
+ auth_ctx,
+ auth_data,
+ auth_data_length,
+ &temp_buf);
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ /* temp_buf has been referenced (and incremented). Only 'own' it
+ * once, at its first value. Since the nist routine called above
+ * bumps it...
+ */
+ temp_buf_flag = SAH_USES_LINK_DATA;
+ } else { /* if NIST */
+ status = sah_Create_Link(user_ctx->mem_util, &link1,
+ (uint8_t *) auth_data,
+ auth_data_length, SAH_USES_LINK_DATA);
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ /* for next/first use of temp_buf */
+ temp_buf_flag = SAH_OWNS_LINK_DATA;
+ cbc_data_length = auth_data_length;
+ } /* else not NIST */
+
+#if defined (FSL_HAVE_SAHARA2) || defined (USE_S2_CCM_ENCRYPT_CHAIN) \
+ || defined (USE_S2_CCM_DECRYPT_CHAIN)
+
+ if (!chain_s2) {
+ header = SAH_HDR_SKHA_CBC_ICV
+ ^ sah_insert_skha_mode_cbc ^ sah_insert_skha_aux0
+ ^ sah_insert_skha_encrypt;
+ } else {
+ /*
+ * Auth data links have been created. Now create link for the
+ * useless output of the CBC calculation.
+ */
+ status = sah_Create_Link(user_ctx->mem_util, &link2,
+ temp_buf,
+ auth_ctx->auth_info.CCM_ctx_info.
+ block_size_bytes,
+ temp_buf_flag | SAH_OUTPUT_LINK);
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ temp_buf += auth_ctx->auth_info.CCM_ctx_info.block_size_bytes;
+
+ cbc_data_length -=
+ auth_ctx->auth_info.CCM_ctx_info.block_size_bytes;
+ if (cbc_data_length != 0) {
+ while ((status == FSL_RETURN_OK_S)
+ && (cbc_data_length != 0)) {
+ uint32_t linklen =
+ (cbc_data_length >
+ CBC_BUF_LEN) ? CBC_BUF_LEN :
+ cbc_data_length;
+
+ status =
+ sah_Append_Link(user_ctx->mem_util, link2,
+ cbc_buffer, linklen,
+ SAH_USES_LINK_DATA |
+ SAH_OUTPUT_LINK);
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ cbc_data_length -= linklen;
+ }
+ }
+ }
+#else
+ header = SAH_HDR_SKHA_CBC_ICV
+ ^ sah_insert_skha_mode_cbc ^ sah_insert_skha_aux0
+ ^ sah_insert_skha_encrypt;
+#endif
+ /* Crank through auth data */
+ status = sah_Append_Desc(user_ctx->mem_util, desc_chain,
+ header, link1, link2);
+
+ out:
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(user_ctx->mem_util, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(user_ctx->mem_util, link2);
+ }
+ }
+
+ (void)encrypt;
+ return status;
+} /* add_assoc_preamble() */
+
+#if SUPPORT_SSL
+/*!
+ * Generate an SSL value
+ *
+ * @param user_ctx Info for acquiring memory
+ * @param auth_ctx Info for CTR0, size of MAC
+ * @param cipher_key_info
+ * @param auth_key_info
+ * @param auth_data_length
+ * @param auth_data
+ * @param payload_length
+ * @param payload
+ * @param ct
+ * @param auth_value
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static fsl_shw_return_t do_ssl_gen(fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * cipher_key_info,
+ fsl_shw_sko_t * auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t * auth_data,
+ uint32_t payload_length,
+ const uint8_t * payload,
+ uint8_t * ct, uint8_t * auth_value)
+{
+ SAH_SF_DCLS;
+ uint8_t *ptr1 = NULL;
+
+ /* Assume one-shot init-finalize... no precomputes */
+ header = SAH_HDR_MDHA_SET_MODE_MD_KEY ^
+ sah_insert_mdha_algorithm[auth_ctx->auth_info.hash_ctx_info.
+ algorithm] ^ sah_insert_mdha_init ^
+ sah_insert_mdha_ssl ^ sah_insert_mdha_pdata ^
+ sah_insert_mdha_mac_full;
+
+ /* set up hmac */
+ DESC_IN_KEY(header, 0, NULL, auth_key_info);
+
+ /* This is wrong -- need to find 16 extra bytes of data from
+ * somewhere */
+ DESC_IN_OUT(SAH_HDR_MDHA_HASH, payload_length, payload, 1, auth_value);
+
+ /* set up encrypt */
+ header = SAH_HDR_SKHA_SET_MODE_IV_KEY
+ ^ sah_insert_skha_mode[auth_ctx->cipher_ctx_info.mode]
+ ^ sah_insert_skha_encrypt
+ ^ sah_insert_skha_algorithm[cipher_key_info->algorithm];
+
+ /* Honor 'no key parity checking' for DES and TDES */
+ if ((cipher_key_info->flags & FSL_SKO_KEY_IGNORE_PARITY) &&
+ ((cipher_key_info->algorithm == FSL_KEY_ALG_DES) ||
+ (cipher_key_info->algorithm == FSL_KEY_ALG_TDES))) {
+ header ^= sah_insert_skha_no_key_parity;
+ }
+
+ if (auth_ctx->cipher_ctx_info.mode == FSL_SYM_MODE_CTR) {
+ header ^=
+ sah_insert_skha_modulus[auth_ctx->cipher_ctx_info.
+ modulus_exp];
+ }
+
+ if ((auth_ctx->cipher_ctx_info.mode == FSL_SYM_MODE_ECB)
+ || (auth_ctx->cipher_ctx_info.flags & FSL_SYM_CTX_INIT)) {
+ ptr1 = block_zeros;
+ } else {
+ ptr1 = auth_ctx->cipher_ctx_info.context;
+ }
+
+ DESC_IN_KEY(header, auth_ctx->cipher_ctx_info.block_size_bytes, ptr1,
+ cipher_key_info);
+
+ /* This is wrong -- need to find 16 extra bytes of data from
+ * somewhere...
+ */
+ if (payload_length != 0) {
+ DESC_IN_OUT(SAH_HDR_SKHA_ENC_DEC,
+ payload_length, payload, payload_length, ct);
+ }
+
+ SAH_SF_EXECUTE();
+
+ out:
+ SAH_SF_DESC_CLEAN();
+
+ /* Eliminate compiler warnings until full implementation... */
+ (void)auth_data;
+ (void)auth_data_length;
+
+ return ret;
+} /* do_ssl_gen() */
+#endif
+
+/*!
+ * @brief Generate a (CCM) auth code and encrypt the payload.
+ *
+ * This is a very complicated function. Seven (or eight) descriptors are
+ * required to perform a CCM calculation.
+ *
+ * First: Load CTR0 and key.
+ *
+ * Second: Run an octet of data through to bump to CTR1. (This could be
+ * done in software, but software will have to bump and later decrement -
+ * or copy and bump.
+ *
+ * Third: (in Virtio) Load a descriptor with data of zeros for CBC IV.
+ *
+ * Fourth: Run any (optional) "additional data" through the CBC-mode
+ * portion of the algorithm.
+ *
+ * Fifth: Run the payload through in CCM mode.
+ *
+ * Sixth: Extract the unencrypted MAC.
+ *
+ * Seventh: Load CTR0.
+ *
+ * Eighth: Encrypt the MAC.
+ *
+ * @param user_ctx The user's context
+ * @param auth_ctx Info on this Auth operation
+ * @param cipher_key_info Key to encrypt payload
+ * @param auth_key_info (unused - same key in CCM)
+ * @param auth_data_length Length in bytes of @a auth_data
+ * @param auth_data Any auth-only data
+ * @param payload_length Length in bytes of @a payload
+ * @param payload The data to encrypt
+ * @param[out] ct The location to store encrypted data
+ * @param[out] auth_value The location to store authentication code
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_gen_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * cipher_key_info,
+ fsl_shw_sko_t * auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t * auth_data,
+ uint32_t payload_length,
+ const uint8_t * payload,
+ uint8_t * ct, uint8_t * auth_value)
+{
+ SAH_SF_DCLS;
+
+ SAH_SF_USER_CHECK();
+
+ if (auth_ctx->mode == FSL_ACC_MODE_SSL) {
+#if SUPPORT_SSL
+ ret = do_ssl_gen(user_ctx, auth_ctx, cipher_key_info,
+ auth_key_info, auth_data_length, auth_data,
+ payload_length, payload, ct, auth_value);
+#else
+ ret = FSL_RETURN_BAD_MODE_S;
+#endif
+ goto out;
+ }
+
+ if (auth_ctx->mode != FSL_ACC_MODE_CCM) {
+ ret = FSL_RETURN_BAD_MODE_S;
+ goto out;
+ }
+
+ /* Only support INIT and FINALIZE flags right now. */
+ if ((auth_ctx->flags & (FSL_ACCO_CTX_INIT | FSL_ACCO_CTX_LOAD |
+ FSL_ACCO_CTX_SAVE | FSL_ACCO_CTX_FINALIZE))
+ != (FSL_ACCO_CTX_INIT | FSL_ACCO_CTX_FINALIZE)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ /* Load CTR0 and Key */
+ header = (SAH_HDR_SKHA_SET_MODE_IV_KEY
+ ^ sah_insert_skha_mode_ctr
+ ^ sah_insert_skha_modulus_128 ^ sah_insert_skha_encrypt);
+ DESC_IN_KEY(header,
+ auth_ctx->cipher_ctx_info.block_size_bytes,
+ auth_ctx->cipher_ctx_info.context, cipher_key_info);
+
+ /* Encrypt dummy data to bump to CTR1 */
+ header = SAH_HDR_SKHA_ENC_DEC;
+ DESC_IN_OUT(header, auth_ctx->mac_length, garbage_output,
+ auth_ctx->mac_length, garbage_output);
+
+#if defined(FSL_HAVE_SAHARA2) || defined(USE_S2_CCM_ENCRYPT_CHAIN)
+#ifndef NO_ZERO_IV_LOAD
+ header = (SAH_HDR_SKHA_SET_MODE_IV_KEY
+ ^ sah_insert_skha_encrypt ^ sah_insert_skha_mode_cbc);
+ DESC_IN_IN(header,
+ auth_ctx->auth_info.CCM_ctx_info.block_size_bytes,
+ block_zeros, 0, NULL);
+#endif
+#endif
+
+ ret = add_assoc_preamble(&desc_chain, user_ctx,
+ auth_ctx, 1, auth_data, auth_data_length);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Process the payload */
+ header = (SAH_HDR_SKHA_SET_MODE_ENC_DEC
+ ^ sah_insert_skha_mode_ccm
+ ^ sah_insert_skha_modulus_128 ^ sah_insert_skha_encrypt);
+#if defined (FSL_HAVE_SAHARA4) && !defined (USE_S2_CCM_ENCRYPT_CHAIN)
+ header ^= sah_insert_skha_aux0;
+#endif
+ if (payload_length != 0) {
+ DESC_IN_OUT(header, payload_length, payload, payload_length,
+ ct);
+ } else {
+ DESC_IN_OUT(header, 0, NULL, 0, NULL);
+ } /* if payload_length */
+
+#if defined (FSL_HAVE_SAHARA4) && !defined (USE_S2_CCM_ENCRYPT_CHAIN)
+ /* Pull out the CBC-MAC value. */
+ DESC_OUT_OUT(SAH_HDR_SKHA_READ_CONTEXT_IV, 0, NULL,
+ auth_ctx->mac_length, auth_value);
+#else
+ /* Pull out the unencrypted CBC-MAC value. */
+ DESC_OUT_OUT(SAH_HDR_SKHA_READ_CONTEXT_IV,
+ 0, NULL, auth_ctx->mac_length, auth_ctx->unencrypted_mac);
+
+ /* Now load CTR0 in, and encrypt the MAC */
+ header = SAH_HDR_SKHA_SET_MODE_IV_KEY
+ ^ sah_insert_skha_encrypt
+ ^ sah_insert_skha_mode_ctr ^ sah_insert_skha_modulus_128;
+ DESC_IN_IN(header,
+ auth_ctx->cipher_ctx_info.block_size_bytes,
+ auth_ctx->cipher_ctx_info.context, 0, NULL);
+
+ header = SAH_HDR_SKHA_ENC_DEC; /* Desc. #4 SKHA Enc/Dec */
+ DESC_IN_OUT(header,
+ auth_ctx->mac_length, auth_ctx->unencrypted_mac,
+ auth_ctx->mac_length, auth_value);
+#endif
+
+ SAH_SF_EXECUTE();
+
+ out:
+ SAH_SF_DESC_CLEAN();
+
+ (void)auth_key_info;
+ return ret;
+} /* fsl_shw_gen_encrypt() */
+
+/*!
+ * @brief Authenticate and decrypt a (CCM) stream.
+ *
+ * @param user_ctx The user's context
+ * @param auth_ctx Info on this Auth operation
+ * @param cipher_key_info Key to encrypt payload
+ * @param auth_key_info (unused - same key in CCM)
+ * @param auth_data_length Length in bytes of @a auth_data
+ * @param auth_data Any auth-only data
+ * @param payload_length Length in bytes of @a payload
+ * @param ct The encrypted data
+ * @param auth_value The authentication code to validate
+ * @param[out] payload The location to store decrypted data
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_auth_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * cipher_key_info,
+ fsl_shw_sko_t * auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t * auth_data,
+ uint32_t payload_length,
+ const uint8_t * ct,
+ const uint8_t * auth_value,
+ uint8_t * payload)
+{
+ SAH_SF_DCLS;
+#if defined(FSL_HAVE_SAHARA2) || defined(USE_S2_CCM_DECRYPT_CHAIN)
+ uint8_t *calced_auth = NULL;
+ unsigned blocking = user_ctx->flags & FSL_UCO_BLOCKING_MODE;
+#endif
+
+ SAH_SF_USER_CHECK();
+
+ /* Only support CCM */
+ if (auth_ctx->mode != FSL_ACC_MODE_CCM) {
+ ret = FSL_RETURN_BAD_MODE_S;
+ goto out;
+ }
+ /* Only support INIT and FINALIZE flags right now. */
+ if ((auth_ctx->flags & (FSL_ACCO_CTX_INIT | FSL_ACCO_CTX_LOAD |
+ FSL_ACCO_CTX_SAVE | FSL_ACCO_CTX_FINALIZE))
+ != (FSL_ACCO_CTX_INIT | FSL_ACCO_CTX_FINALIZE)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ /* Load CTR0 and Key */
+ header = SAH_HDR_SKHA_SET_MODE_IV_KEY
+ ^ sah_insert_skha_mode_ctr ^ sah_insert_skha_modulus_128;
+#if defined (FSL_HAVE_SAHARA4) && !defined (USE_S2_CCM_DECRYPT_CHAIN)
+ header ^= sah_insert_skha_aux0;
+#endif
+ DESC_IN_KEY(header,
+ auth_ctx->cipher_ctx_info.block_size_bytes,
+ auth_ctx->cipher_ctx_info.context, cipher_key_info);
+
+ /* Decrypt the MAC which the user passed in */
+ header = SAH_HDR_SKHA_ENC_DEC;
+ DESC_IN_OUT(header,
+ auth_ctx->mac_length, auth_value,
+ auth_ctx->mac_length, auth_ctx->unencrypted_mac);
+
+#if defined(FSL_HAVE_SAHARA2) || defined(USE_S2_CCM_DECRYPT_CHAIN)
+#ifndef NO_ZERO_IV_LOAD
+ header = (SAH_HDR_SKHA_SET_MODE_IV_KEY
+ ^ sah_insert_skha_encrypt ^ sah_insert_skha_mode_cbc);
+ DESC_IN_IN(header,
+ auth_ctx->auth_info.CCM_ctx_info.block_size_bytes,
+ block_zeros, 0, NULL);
+#endif
+#endif
+
+ ret = add_assoc_preamble(&desc_chain, user_ctx,
+ auth_ctx, 0, auth_data, auth_data_length);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Process the payload */
+ header = (SAH_HDR_SKHA_SET_MODE_ENC_DEC
+ ^ sah_insert_skha_mode_ccm ^ sah_insert_skha_modulus_128);
+#if defined (FSL_HAVE_SAHARA4) && !defined (USE_S2_CCM_DECRYPT_CHAIN)
+ header ^= sah_insert_skha_aux0;
+#endif
+ if (payload_length != 0) {
+ DESC_IN_OUT(header, payload_length, ct, payload_length,
+ payload);
+ } else {
+ DESC_IN_OUT(header, 0, NULL, 0, NULL);
+ }
+
+#if defined (FSL_HAVE_SAHARA2) || defined (USE_S2_CCM_DECRYPT_CHAIN)
+ /* Now pull CBC context (unencrypted MAC) out for comparison. */
+ /* Need to allocate a place for it, to handle non-blocking mode
+ * when this stack frame will disappear!
+ */
+ calced_auth = DESC_TEMP_ALLOC(auth_ctx->mac_length);
+ header = SAH_HDR_SKHA_READ_CONTEXT_IV;
+ DESC_OUT_OUT(header, 0, NULL, auth_ctx->mac_length, calced_auth);
+ if (!blocking) {
+ /* get_results will need this for comparison */
+ desc_chain->out1_ptr = calced_auth;
+ desc_chain->out2_ptr = auth_ctx->unencrypted_mac;
+ desc_chain->out_len = auth_ctx->mac_length;
+ }
+#endif
+
+ SAH_SF_EXECUTE();
+
+#if defined (FSL_HAVE_SAHARA2) || defined (USE_S2_CCM_DECRYPT_CHAIN)
+ if (blocking && (ret == FSL_RETURN_OK_S)) {
+ unsigned i;
+ /* Validate the auth code */
+ for (i = 0; i < auth_ctx->mac_length; i++) {
+ if (calced_auth[i] != auth_ctx->unencrypted_mac[i]) {
+ ret = FSL_RETURN_AUTH_FAILED_S;
+ break;
+ }
+ }
+ }
+#endif
+
+ out:
+ SAH_SF_DESC_CLEAN();
+#if defined (FSL_HAVE_SAHARA2) || defined (USE_S2_CCM_DECRYPT_CHAIN)
+ DESC_TEMP_FREE(calced_auth);
+#endif
+
+ (void)auth_key_info;
+ return ret;
+} /* fsl_shw_gen_decrypt() */
diff --git a/drivers/mxc/security/sahara2/fsl_shw_hash.c b/drivers/mxc/security/sahara2/fsl_shw_hash.c
new file mode 100644
index 000000000000..7d4db53480f6
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_hash.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file fsl_shw_hash.c
+ *
+ * This file implements Cryptographic Hashing functions of the FSL SHW API
+ * for Sahara. This does not include HMAC.
+ */
+
+#include "sahara.h"
+#include "sf_util.h"
+
+#ifdef LINUX_KERNEL
+EXPORT_SYMBOL(fsl_shw_hash);
+#endif
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-005 */
+/*!
+ * Hash a stream of data with a cryptographic hash algorithm.
+ *
+ * The flags in the @a hash_ctx control the operation of this function.
+ *
+ * Hashing functions work on 64 octets of message at a time. Therefore, when
+ * any partial hashing of a long message is performed, the message @a length of
+ * each segment must be a multiple of 64. When ready to
+ * #FSL_HASH_FLAGS_FINALIZE the hash, the @a length may be any value.
+ *
+ * With the #FSL_HASH_FLAGS_INIT and #FSL_HASH_FLAGS_FINALIZE flags on, a
+ * one-shot complete hash, including padding, will be performed. The @a length
+ * may be any value.
+ *
+ * The first octets of a data stream can be hashed by setting the
+ * #FSL_HASH_FLAGS_INIT and #FSL_HASH_FLAGS_SAVE flags. The @a length must be
+ * a multiple of 64.
+ *
+ * The flag #FSL_HASH_FLAGS_LOAD is used to load a context previously saved by
+ * #FSL_HASH_FLAGS_SAVE. The two in combination will allow a (multiple-of-64
+ * octets) 'middle sequence' of the data stream to be hashed with the
+ * beginning. The @a length must again be a multiple of 64.
+ *
+ * Since the flag #FSL_HASH_FLAGS_LOAD is used to load a context previously
+ * saved by #FSL_HASH_FLAGS_SAVE, the #FSL_HASH_FLAGS_LOAD and
+ * #FSL_HASH_FLAGS_FINALIZE flags, used together, can be used to finish the
+ * stream. The @a length may be any value.
+ *
+ * If the user program wants to do the padding for the hash, it can leave off
+ * the #FSL_HASH_FLAGS_FINALIZE flag. The @a length must then be a multiple of
+ * 64 octets.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] hash_ctx Hashing algorithm and state of the cipher.
+ * @param msg Pointer to the data to be hashed.
+ * @param length Length, in octets, of the @a msg.
+ * @param[out] result If not null, pointer to where to store the hash
+ * digest.
+ * @param result_len Number of octets to store in @a result.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_hash(fsl_shw_uco_t * user_ctx,
+ fsl_shw_hco_t * hash_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len)
+{
+ SAH_SF_DCLS;
+ unsigned ctx_flags = (hash_ctx->flags & (FSL_HASH_FLAGS_INIT
+ | FSL_HASH_FLAGS_LOAD
+ | FSL_HASH_FLAGS_SAVE
+ | FSL_HASH_FLAGS_FINALIZE));
+
+ SAH_SF_USER_CHECK();
+
+ /* Reset expectations if user gets overly zealous. */
+ if (result_len > hash_ctx->digest_length) {
+ result_len = hash_ctx->digest_length;
+ }
+
+ /* Validate hash ctx flags.
+ * Need INIT or LOAD but not both.
+ * Need SAVE or digest ptr (both is ok).
+ */
+ if (((ctx_flags & (FSL_HASH_FLAGS_INIT | FSL_HASH_FLAGS_LOAD))
+ == (FSL_HASH_FLAGS_INIT | FSL_HASH_FLAGS_LOAD))
+ || ((ctx_flags & (FSL_HASH_FLAGS_INIT | FSL_HASH_FLAGS_LOAD)) == 0)
+ || (!(ctx_flags & FSL_HASH_FLAGS_SAVE) && (result == NULL))) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ if (ctx_flags & FSL_HASH_FLAGS_INIT) {
+ sah_Oct_Str out_ptr;
+ unsigned out_len;
+
+ /* Create desc to perform the initial hashing operation */
+ /* Desc. #8 w/INIT and algorithm */
+ header = SAH_HDR_MDHA_SET_MODE_HASH
+ ^ sah_insert_mdha_init
+ ^ sah_insert_mdha_algorithm[hash_ctx->algorithm];
+
+ /* If user wants one-shot, set padding operation. */
+ if (ctx_flags & FSL_HASH_FLAGS_FINALIZE) {
+ header ^= sah_insert_mdha_pdata;
+ }
+
+ /* Determine where Digest will go - hash_ctx or result */
+ if (ctx_flags & FSL_HASH_FLAGS_SAVE) {
+ out_ptr = (sah_Oct_Str) hash_ctx->context;
+ out_len = hash_ctx->context_register_length;
+ } else {
+ out_ptr = result;
+ out_len = (result_len > hash_ctx->digest_length)
+ ? hash_ctx->digest_length : result_len;
+ }
+
+ DESC_IN_OUT(header, length, (sah_Oct_Str) msg, out_len,
+ out_ptr);
+ } else { /* not doing hash INIT */
+ void *out_ptr;
+ unsigned out_len;
+
+ /*
+ * Build two descriptors -- one to load in context/set mode, the
+ * other to compute & retrieve hash/context value.
+ *
+ * First up - Desc. #6 to load context.
+ */
+ /* Desc. #8 w/algorithm */
+ header = SAH_HDR_MDHA_SET_MODE_MD_KEY
+ ^ sah_insert_mdha_algorithm[hash_ctx->algorithm];
+
+ if (ctx_flags & FSL_HASH_FLAGS_FINALIZE) {
+ header ^= sah_insert_mdha_pdata;
+ }
+
+ /* Message Digest (in) */
+ DESC_IN_IN(header,
+ hash_ctx->context_register_length,
+ (sah_Oct_Str) hash_ctx->context, 0, NULL);
+
+ if (ctx_flags & FSL_HASH_FLAGS_SAVE) {
+ out_ptr = hash_ctx->context;
+ out_len = hash_ctx->context_register_length;
+ } else {
+ out_ptr = result;
+ out_len = result_len;
+ }
+
+ /* Second -- run data through and retrieve ctx regs */
+ /* Desc. #10 - no mode register with this. */
+ header = SAH_HDR_MDHA_HASH;
+ DESC_IN_OUT(header, length, (sah_Oct_Str) msg, out_len,
+ out_ptr);
+ } /* else not INIT */
+
+ /* Now that execution is rejoined, we can append another descriptor
+ to extract the digest/context a second time, into the result. */
+ if ((ctx_flags & FSL_HASH_FLAGS_SAVE)
+ && (result != NULL) && (result_len != 0)) {
+
+ header = SAH_HDR_MDHA_STORE_DIGEST;
+
+ /* Message Digest (out) */
+ DESC_IN_OUT(header, 0, NULL,
+ (result_len > hash_ctx->digest_length)
+ ? hash_ctx->digest_length : result_len, result);
+ }
+
+ SAH_SF_EXECUTE();
+
+ out:
+ SAH_SF_DESC_CLEAN();
+
+ return ret;
+} /* fsl_shw_hash() */
diff --git a/drivers/mxc/security/sahara2/fsl_shw_hmac.c b/drivers/mxc/security/sahara2/fsl_shw_hmac.c
new file mode 100644
index 000000000000..21f4314054d6
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_hmac.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file fsl_shw_hmac.c
+ *
+ * This file implements Hashed Message Authentication Code functions of the FSL
+ * SHW API for Sahara.
+ */
+
+#include "sahara.h"
+#include "sf_util.h"
+
+#ifdef __KERNEL__
+EXPORT_SYMBOL(fsl_shw_hmac_precompute);
+EXPORT_SYMBOL(fsl_shw_hmac);
+#endif
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HMAC-001 */
+/*!
+ * Get the precompute information
+ *
+ *
+ * @param user_ctx
+ * @param key_info
+ * @param hmac_ctx
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_hmac_precompute(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx)
+{
+ SAH_SF_DCLS;
+
+ SAH_SF_USER_CHECK();
+
+ if ((key_info->algorithm != FSL_KEY_ALG_HMAC) ||
+ (key_info->key_length > 64)) {
+ return FSL_RETURN_BAD_ALGORITHM_S;
+ } else if (key_info->key_length == 0) {
+ return FSL_RETURN_BAD_KEY_LENGTH_S;
+ }
+
+ /* Set up to start the Inner Calculation */
+ /* Desc. #8 w/IPAD, & INIT */
+ header = SAH_HDR_MDHA_SET_MODE_HASH
+ ^ sah_insert_mdha_ipad
+ ^ sah_insert_mdha_init
+ ^ sah_insert_mdha_algorithm[hmac_ctx->algorithm];
+
+ DESC_KEY_OUT(header, key_info,
+ hmac_ctx->context_register_length,
+ (uint8_t *) hmac_ctx->inner_precompute);
+
+ /* Set up for starting Outer calculation */
+ /* exchange IPAD bit for OPAD bit */
+ header ^= (sah_insert_mdha_ipad ^ sah_insert_mdha_opad);
+
+ /* Theoretically, we can leave this link out and use the key which is
+ * already in the register... however, if we do, the resulting opad
+ * hash does not have the correct value when using the model. */
+ DESC_KEY_OUT(header, key_info,
+ hmac_ctx->context_register_length,
+ (uint8_t *) hmac_ctx->outer_precompute);
+
+ SAH_SF_EXECUTE();
+ if (ret == FSL_RETURN_OK_S) {
+ /* flag that precomputes have been entered in this hco
+ * assume it'll first be used for initilizing */
+ hmac_ctx->flags |= (FSL_HMAC_FLAGS_INIT |
+ FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT);
+ }
+
+ out:
+ SAH_SF_DESC_CLEAN();
+
+ return ret;
+}
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HMAC-002 */
+/*!
+ * Get the hmac
+ *
+ *
+ * @param user_ctx Info for acquiring memory
+ * @param key_info
+ * @param hmac_ctx
+ * @param msg
+ * @param length
+ * @param result
+ * @param result_len
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_hmac(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len)
+{
+ SAH_SF_DCLS;
+
+ SAH_SF_USER_CHECK();
+
+ /* check flag consistency */
+ /* Note that Final, Init, and Save are an illegal combination when a key
+ * is being used. Because of the logic flow of this routine, that is
+ * taken care of without being explict */
+ if (
+ /* nothing to work on */
+ (((hmac_ctx->flags & FSL_HMAC_FLAGS_INIT) == 0) &&
+ ((hmac_ctx->flags & FSL_HMAC_FLAGS_LOAD) == 0)) ||
+ /* can't do both */
+ ((hmac_ctx->flags & FSL_HMAC_FLAGS_INIT) &&
+ (hmac_ctx->flags & FSL_HMAC_FLAGS_LOAD)) ||
+ /* must be some output */
+ (((hmac_ctx->flags & FSL_HMAC_FLAGS_SAVE) == 0) &&
+ ((hmac_ctx->flags & FSL_HMAC_FLAGS_FINALIZE) == 0))) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ /* build descriptor #6 */
+
+ /* start building descriptor header */
+ header = SAH_HDR_MDHA_SET_MODE_MD_KEY ^
+ sah_insert_mdha_algorithm[hmac_ctx->algorithm] ^
+ sah_insert_mdha_init;
+
+ /* if this is to finalize the digest, mark to pad last block */
+ if (hmac_ctx->flags & FSL_HMAC_FLAGS_FINALIZE) {
+ header ^= sah_insert_mdha_pdata;
+ }
+
+ /* Check if this is a one shot */
+ if ((hmac_ctx->flags & FSL_HMAC_FLAGS_INIT) &&
+ (hmac_ctx->flags & FSL_HMAC_FLAGS_FINALIZE)) {
+
+ header ^= sah_insert_mdha_hmac;
+
+ /* See if this uses Precomputes */
+ if (hmac_ctx->flags & FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT) {
+ DESC_IN_IN(header,
+ hmac_ctx->context_register_length,
+ (uint8_t *) hmac_ctx->inner_precompute,
+ hmac_ctx->context_length,
+ (uint8_t *) hmac_ctx->outer_precompute);
+ } else { /* Precomputes not requested, try using Key */
+ if (key_info->key != NULL) {
+ /* first, validate the key fields and related flag */
+ if ((key_info->key_length == 0)
+ || (key_info->key_length > 64)) {
+ ret = FSL_RETURN_BAD_KEY_LENGTH_S;
+ goto out;
+ } else {
+ if (key_info->algorithm !=
+ FSL_KEY_ALG_HMAC) {
+ ret =
+ FSL_RETURN_BAD_ALGORITHM_S;
+ goto out;
+ }
+ }
+
+ /* finish building descriptor header (Key specific) */
+ header ^= sah_insert_mdha_mac_full;
+ DESC_IN_KEY(header, 0, NULL, key_info);
+ } else { /* not using Key or Precomputes, so die */
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+ }
+ } else { /* it's not a one shot, must be multi-step */
+ /* this the last chunk? */
+ if (hmac_ctx->flags & FSL_HMAC_FLAGS_FINALIZE) {
+ header ^= sah_insert_mdha_hmac;
+ DESC_IN_IN(header,
+ hmac_ctx->context_register_length,
+ (uint8_t *) hmac_ctx->ongoing_context,
+ hmac_ctx->context_length,
+ (uint8_t *) hmac_ctx->outer_precompute);
+ } else { /* not last chunk */
+ uint8_t *ptr1;
+
+ if (hmac_ctx->flags & FSL_HMAC_FLAGS_INIT) {
+ /* must be using precomputes, cannot 'chunk' message
+ * starting with a key */
+ if (hmac_ctx->
+ flags & FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT)
+ {
+ ptr1 =
+ (uint8_t *) hmac_ctx->
+ inner_precompute;
+ } else {
+ ret = FSL_RETURN_NO_RESOURCE_S;
+ goto out;
+ }
+ } else {
+ ptr1 = (uint8_t *) hmac_ctx->ongoing_context;
+ }
+
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ DESC_IN_IN(header,
+ hmac_ctx->context_register_length, ptr1,
+ 0, NULL);
+ }
+ } /* multi-step */
+
+ /* build descriptor #10 & maybe 11 */
+ header = SAH_HDR_MDHA_HASH;
+
+ if (hmac_ctx->flags & FSL_HMAC_FLAGS_FINALIZE) {
+ /* check that the results parameters seem reasonable */
+ if ((result_len != 0) && (result != NULL)) {
+ if (result_len > hmac_ctx->context_register_length) {
+ result_len = hmac_ctx->context_register_length;
+ }
+
+ /* message in / digest out (descriptor #10) */
+ DESC_IN_OUT(header, length, msg, result_len, result);
+
+ /* see if descriptor #11 needs to be built */
+ if (hmac_ctx->flags & FSL_HMAC_FLAGS_SAVE) {
+ header = SAH_HDR_MDHA_STORE_DIGEST;
+ /* nothing in / context out */
+ DESC_IN_IN(header, 0, NULL,
+ hmac_ctx->context_register_length,
+ (uint8_t *) hmac_ctx->
+ ongoing_context);
+ }
+ } else {
+ /* something wrong with result or its length */
+ ret = FSL_RETURN_BAD_DATA_LENGTH_S;
+ }
+ } else { /* finalize not set, so store in ongoing context field */
+ if ((length % 64) == 0) { /* this will change for 384/512 support */
+ /* message in / context out */
+ DESC_IN_OUT(header, length, msg,
+ hmac_ctx->context_register_length,
+ (uint8_t *) hmac_ctx->ongoing_context);
+ } else {
+ /* not final data, and not multiple of block length */
+ ret = FSL_RETURN_BAD_DATA_LENGTH_S;
+ }
+ }
+
+ SAH_SF_EXECUTE();
+
+ out:
+ SAH_SF_DESC_CLEAN();
+
+ return ret;
+} /* fsl_shw_hmac() */
diff --git a/drivers/mxc/security/sahara2/fsl_shw_keystore.c b/drivers/mxc/security/sahara2/fsl_shw_keystore.c
new file mode 100644
index 000000000000..b45cda6dde01
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_keystore.c
@@ -0,0 +1,837 @@
+/*
+ * Copyright (C) 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * @file fsl_shw_keystore.c
+ *
+ * File which implements a default keystore policy, for use as the system
+ * keystore.
+ */
+#include "fsl_platform.h"
+#include "fsl_shw.h"
+#include "fsl_shw_keystore.h"
+
+#if defined(DIAG_DRV_IF)
+#include <diagnostic.h>
+#endif
+
+#if !defined(FSL_HAVE_SCC2) && defined(__KERNEL__)
+#include <linux/mxc_scc_driver.h>
+#endif
+
+/* Define a semaphore to protect the keystore data */
+#ifdef __KERNEL__
+#define LOCK_INCLUDES os_lock_context_t context
+#define ACQUIRE_LOCK os_lock_save_context(keystore->lock, context)
+#define RELEASE_LOCK os_unlock_restore_context(keystore->lock, context);
+#else
+#define LOCK_INCLUDES
+#define ACQUIRE_LOCK
+#define RELEASE_LOCK
+#endif /* __KERNEL__ */
+
+/*!
+ * Calculates the byte offset into a word
+ * @param bp The byte (char*) pointer
+ * @return The offset (0, 1, 2, or 3)
+ */
+#define SCC_BYTE_OFFSET(bp) ((uint32_t)(bp) % sizeof(uint32_t))
+
+/*!
+ * Converts (by rounding down) a byte pointer into a word pointer
+ * @param bp The byte (char*) pointer
+ * @return The word (uint32_t) as though it were an aligned (uint32_t*)
+ */
+#define SCC_WORD_PTR(bp) (((uint32_t)(bp)) & ~(sizeof(uint32_t)-1))
+
+/* Depending on the architecture, these functions should be defined
+ * differently. On Platforms with SCC2, the functions use the secure
+ * partition interface and should be available in both user and kernel space.
+ * On platforms with SCC, they use the SCC keystore interface. This is only
+ * available in kernel mode, so they should be stubbed out in user mode.
+ */
+#if defined(FSL_HAVE_SCC2) || (defined(FSL_HAVE_SCC) && defined(__KERNEL__))
+EXPORT_SYMBOL(fsl_shw_init_keystore);
+void fsl_shw_init_keystore(
+ fsl_shw_kso_t *keystore,
+ fsl_shw_return_t(*data_init) (fsl_shw_uco_t *user_ctx,
+ void **user_data),
+ void (*data_cleanup) (fsl_shw_uco_t *user_ctx,
+ void **user_data),
+ fsl_shw_return_t(*slot_alloc) (void *user_data,
+ uint32_t size,
+ uint64_t owner_id,
+ uint32_t *slot),
+ fsl_shw_return_t(*slot_dealloc) (void *user_data,
+ uint64_t
+ owner_id,
+ uint32_t slot),
+ fsl_shw_return_t(*slot_verify_access) (void
+ *user_data,
+ uint64_t
+ owner_id,
+ uint32_t
+ slot),
+ void *(*slot_get_address) (void *user_data,
+ uint32_t handle),
+ uint32_t(*slot_get_base) (void *user_data,
+ uint32_t handle),
+ uint32_t(*slot_get_offset) (void *user_data,
+ uint32_t handle),
+ uint32_t(*slot_get_slot_size) (void *user_data,
+ uint32_t handle))
+{
+ keystore->data_init = data_init;
+ keystore->data_cleanup = data_cleanup;
+ keystore->slot_alloc = slot_alloc;
+ keystore->slot_dealloc = slot_dealloc;
+ keystore->slot_verify_access = slot_verify_access;
+ keystore->slot_get_address = slot_get_address;
+ keystore->slot_get_base = slot_get_base;
+ keystore->slot_get_offset = slot_get_offset;
+ keystore->slot_get_slot_size = slot_get_slot_size;
+}
+
+EXPORT_SYMBOL(fsl_shw_init_keystore_default);
+void fsl_shw_init_keystore_default(fsl_shw_kso_t *keystore)
+{
+ keystore->data_init = shw_kso_init_data;
+ keystore->data_cleanup = shw_kso_cleanup_data;
+ keystore->slot_alloc = shw_slot_alloc;
+ keystore->slot_dealloc = shw_slot_dealloc;
+ keystore->slot_verify_access = shw_slot_verify_access;
+ keystore->slot_get_address = shw_slot_get_address;
+ keystore->slot_get_base = shw_slot_get_base;
+ keystore->slot_get_offset = shw_slot_get_offset;
+ keystore->slot_get_slot_size = shw_slot_get_slot_size;
+}
+
+/*!
+ * Do any keystore specific initializations
+ */
+EXPORT_SYMBOL(fsl_shw_establish_keystore);
+fsl_shw_return_t fsl_shw_establish_keystore(fsl_shw_uco_t *user_ctx,
+ fsl_shw_kso_t *keystore)
+{
+ if (keystore->data_init == NULL) {
+ return FSL_RETURN_ERROR_S;
+ }
+
+ /* Call the data_init function for any user setup */
+ return keystore->data_init(user_ctx, &(keystore->user_data));
+}
+
+EXPORT_SYMBOL(fsl_shw_release_keystore);
+void fsl_shw_release_keystore(fsl_shw_uco_t *user_ctx,
+ fsl_shw_kso_t *keystore)
+{
+
+ /* Call the data_cleanup function for any keystore cleanup.
+ * NOTE: The keystore doesn't have any way of telling which keys are using
+ * it, so it is up to the user program to manage their key objects
+ * correctly.
+ */
+ if ((keystore != NULL) && (keystore->data_cleanup != NULL)) {
+ keystore->data_cleanup(user_ctx, &(keystore->user_data));
+ }
+ return;
+}
+
+fsl_shw_return_t keystore_slot_alloc(fsl_shw_kso_t *keystore, uint32_t size,
+ uint64_t owner_id, uint32_t *slot)
+{
+ LOCK_INCLUDES;
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+
+#ifdef DIAG_DRV_IF
+ LOG_DIAG("In keystore_slot_alloc.");
+
+#endif
+ ACQUIRE_LOCK;
+ if ((keystore->slot_alloc == NULL) || (keystore->user_data == NULL)) {
+ goto out;
+ }
+
+#ifdef DIAG_DRV_IF
+ LOG_DIAG_ARGS("key length: %i, handle: %i\n", size, *slot);
+
+#endif
+retval = keystore->slot_alloc(keystore->user_data, size, owner_id, slot);
+out:RELEASE_LOCK;
+ return retval;
+}
+
+fsl_shw_return_t keystore_slot_dealloc(fsl_shw_kso_t *keystore,
+ uint64_t owner_id, uint32_t slot)
+{
+ LOCK_INCLUDES;
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+ ACQUIRE_LOCK;
+ if ((keystore->slot_alloc == NULL) || (keystore->user_data == NULL)) {
+ goto out;
+ }
+ retval =
+ keystore->slot_dealloc(keystore->user_data, owner_id, slot);
+out:RELEASE_LOCK;
+ return retval;
+}
+
+fsl_shw_return_t
+keystore_slot_load(fsl_shw_kso_t * keystore, uint64_t owner_id, uint32_t slot,
+ const uint8_t * key_data, uint32_t key_length)
+{
+
+#ifdef FSL_HAVE_SCC2
+ LOCK_INCLUDES;
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+ uint32_t slot_size;
+ uint32_t i;
+ uint8_t * slot_location;
+ ACQUIRE_LOCK;
+ if ((keystore->slot_verify_access == NULL) ||
+ (keystore->user_data == NULL))
+ goto out;
+ if (keystore->
+ slot_verify_access(keystore->user_data, owner_id,
+ slot) !=FSL_RETURN_OK_S) {
+ retval = FSL_RETURN_AUTH_FAILED_S;
+ goto out;
+ }
+ slot_size = keystore->slot_get_slot_size(keystore->user_data, slot);
+ if (key_length > slot_size) {
+ retval = FSL_RETURN_BAD_DATA_LENGTH_S;
+ goto out;
+ }
+ slot_location = keystore->slot_get_address(keystore->user_data, slot);
+ for (i = 0; i < key_length; i++) {
+ slot_location[i] = key_data[i];
+ }
+ retval = FSL_RETURN_OK_S;
+out:RELEASE_LOCK;
+ return retval;
+
+#else /* FSL_HAVE_SCC2 */
+ fsl_shw_return_t retval;
+ scc_return_t scc_ret;
+ scc_ret =
+ scc_load_slot(owner_id, slot, (uint8_t *) key_data, key_length);
+ switch (scc_ret) {
+ case SCC_RET_OK:
+ retval = FSL_RETURN_OK_S;
+ break;
+ case SCC_RET_VERIFICATION_FAILED:
+ retval = FSL_RETURN_AUTH_FAILED_S;
+ break;
+ case SCC_RET_INSUFFICIENT_SPACE:
+ retval = FSL_RETURN_BAD_DATA_LENGTH_S;
+ break;
+ default:
+ retval = FSL_RETURN_ERROR_S;
+ }
+ return retval;
+
+#endif /* FSL_HAVE_SCC2 */
+}
+
+fsl_shw_return_t
+keystore_slot_read(fsl_shw_kso_t * keystore, uint64_t owner_id, uint32_t slot,
+ uint32_t key_length, uint8_t * key_data)
+{
+#ifdef FSL_HAVE_SCC2
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+ uint8_t *slot_addr;
+ uint32_t slot_size;
+
+ slot_addr = keystore->slot_get_address(keystore->user_data, slot);
+ slot_size = keystore->slot_get_slot_size(keystore->user_data, slot);
+
+ if (key_length > slot_size) {
+ retval = FSL_RETURN_BAD_KEY_LENGTH_S;
+ goto out;
+ }
+
+ memcpy(key_data, slot_addr, key_length);
+ retval = FSL_RETURN_OK_S;
+
+ out:
+ return retval;
+
+#else /* Have SCC2 */
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+ scc_return_t scc_ret;
+ printk("keystore SCC \n");
+
+ scc_ret =
+ scc_read_slot(owner_id, slot, key_length, (uint8_t *) key_data);
+ printk("keystore SCC Ret value: %d \n", scc_ret);
+ switch (scc_ret) {
+ case SCC_RET_OK:
+ retval = FSL_RETURN_OK_S;
+ break;
+ case SCC_RET_VERIFICATION_FAILED:
+ retval = FSL_RETURN_AUTH_FAILED_S;
+ break;
+ case SCC_RET_INSUFFICIENT_SPACE:
+ retval = FSL_RETURN_BAD_DATA_LENGTH_S;
+ break;
+ default:
+ retval = FSL_RETURN_ERROR_S;
+ }
+
+ return retval;
+
+#endif /* FSL_HAVE_SCC2 */
+}/* end fn keystore_slot_read */
+
+fsl_shw_return_t
+keystore_slot_encrypt(fsl_shw_uco_t *user_ctx, fsl_shw_kso_t *keystore,
+ uint64_t owner_id, uint32_t slot, uint32_t length,
+ uint8_t *destination)
+{
+
+#ifdef FSL_HAVE_SCC2
+ LOCK_INCLUDES;
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+ uint32_t slot_length;
+ uint32_t IV[4];
+ uint32_t * iv_ptr = (uint32_t *) & (owner_id);
+
+ /* Build the IV */
+ IV[0] = iv_ptr[0];
+ IV[1] = iv_ptr[1];
+ IV[2] = 0;
+ IV[3] = 0;
+ ACQUIRE_LOCK;
+
+ /* Ensure that the data will fit in the key slot */
+ slot_length =
+ keystore->slot_get_slot_size(keystore->user_data, slot);
+ if (length > slot_length) {
+ goto out;
+ }
+
+ /* Call scc encrypt function to encrypt the data. */
+ retval = do_scc_encrypt_region(user_ctx,
+ (void *)keystore->
+ slot_get_base(keystore->user_data,
+ slot),
+ keystore->slot_get_offset(keystore->
+ user_data,
+ slot),
+ length, destination, IV,
+ FSL_SHW_CYPHER_MODE_CBC);
+ goto out;
+out:RELEASE_LOCK;
+ return retval;
+
+#else
+ scc_return_t retval;
+ retval = scc_encrypt_slot(owner_id, slot, length, destination);
+ if (retval == SCC_RET_OK)
+ return FSL_RETURN_OK_S;
+ return FSL_RETURN_ERROR_S;
+
+#endif /* FSL_HAVE_SCC2 */
+}
+
+fsl_shw_return_t
+keystore_slot_decrypt(fsl_shw_uco_t *user_ctx, fsl_shw_kso_t *keystore,
+ uint64_t owner_id, uint32_t slot, uint32_t length,
+ const uint8_t *source)
+{
+
+#ifdef FSL_HAVE_SCC2
+ LOCK_INCLUDES;
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+ uint32_t slot_length;
+ uint32_t IV[4];
+ uint32_t *iv_ptr = (uint32_t *) & (owner_id);
+
+ /* Build the IV */
+ IV[0] = iv_ptr[0];
+ IV[1] = iv_ptr[1];
+ IV[2] = 0;
+ IV[3] = 0;
+ ACQUIRE_LOCK;
+
+ /* Call scc decrypt function to decrypt the data. */
+
+ /* Ensure that the data will fit in the key slot */
+ slot_length =
+ keystore->slot_get_slot_size(keystore->user_data, slot);
+ if (length > slot_length)
+ goto out;
+
+ /* Call scc decrypt function to encrypt the data. */
+ retval = do_scc_decrypt_region(user_ctx,
+ (void *)keystore->
+ slot_get_base(keystore->user_data,
+ slot),
+ keystore->slot_get_offset(keystore->
+ user_data,
+ slot),
+ length, source, IV,
+ FSL_SHW_CYPHER_MODE_CBC);
+ goto out;
+out:RELEASE_LOCK;
+ return retval;
+
+#else
+ scc_return_t retval;
+ retval = scc_decrypt_slot(owner_id, slot, length, source);
+ if (retval == SCC_RET_OK)
+ return FSL_RETURN_OK_S;
+ return FSL_RETURN_ERROR_S;
+
+#endif /* FSL_HAVE_SCC2 */
+}
+
+#else /* SCC in userspace */
+void fsl_shw_init_keystore(
+ fsl_shw_kso_t *keystore,
+ fsl_shw_return_t(*data_init) (fsl_shw_uco_t *user_ctx,
+ void **user_data),
+ void (*data_cleanup) (fsl_shw_uco_t *user_ctx,
+ void **user_data),
+ fsl_shw_return_t(*slot_alloc) (void *user_data,
+ uint32_t size,
+ uint64_t owner_id,
+ uint32_t *slot),
+ fsl_shw_return_t(*slot_dealloc) (void *user_data,
+ uint64_t
+ owner_id,
+ uint32_t slot),
+ fsl_shw_return_t(*slot_verify_access) (void
+ *user_data,
+ uint64_t
+ owner_id,
+ uint32_t
+ slot),
+ void *(*slot_get_address) (void *user_data,
+ uint32_t handle),
+ uint32_t(*slot_get_base) (void *user_data,
+ uint32_t handle),
+ uint32_t(*slot_get_offset) (void *user_data,
+ uint32_t handle),
+ uint32_t(*slot_get_slot_size) (void *user_data,
+ uint32_t handle))
+{
+ (void)keystore;
+ (void)data_init;
+ (void)data_cleanup;
+ (void)slot_alloc;
+ (void)slot_dealloc;
+ (void)slot_verify_access;
+ (void)slot_get_address;
+ (void)slot_get_base;
+ (void)slot_get_offset;
+ (void)slot_get_slot_size;
+}
+
+void fsl_shw_init_keystore_default(fsl_shw_kso_t * keystore)
+{
+ (void)keystore;
+}
+fsl_shw_return_t fsl_shw_establish_keystore(fsl_shw_uco_t *user_ctx,
+ fsl_shw_kso_t *keystore)
+{
+ (void)user_ctx;
+ (void)keystore;
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+void fsl_shw_release_keystore(fsl_shw_uco_t *user_ctx,
+ fsl_shw_kso_t *keystore)
+{
+ (void)user_ctx;
+ (void)keystore;
+ return;
+}
+
+fsl_shw_return_t keystore_slot_alloc(fsl_shw_kso_t *keystore, uint32_t size,
+ uint64_t owner_id, uint32_t *slot)
+{
+ (void)keystore;
+ (void)size;
+ (void)owner_id;
+ (void)slot;
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t keystore_slot_dealloc(fsl_shw_kso_t *keystore,
+ uint64_t owner_id, uint32_t slot)
+{
+ (void)keystore;
+ (void)owner_id;
+ (void)slot;
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t
+keystore_slot_load(fsl_shw_kso_t *keystore, uint64_t owner_id, uint32_t slot,
+ const uint8_t *key_data, uint32_t key_length)
+{
+ (void)keystore;
+ (void)owner_id;
+ (void)slot;
+ (void)key_data;
+ (void)key_length;
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t
+keystore_slot_read(fsl_shw_kso_t * keystore, uint64_t owner_id, uint32_t slot,
+ uint32_t key_length, uint8_t * key_data)
+{
+ (void)keystore;
+ (void)owner_id;
+ (void)slot;
+ (void)key_length;
+ (void)key_data;
+
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t
+keystore_slot_decrypt(fsl_shw_uco_t *user_ctx, fsl_shw_kso_t *keystore,
+ uint64_t owner_id, uint32_t slot, uint32_t length,
+ const uint8_t *source)
+{
+ (void)user_ctx;
+ (void)keystore;
+ (void)owner_id;
+ (void)slot;
+ (void)length;
+ (void)source;
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t
+keystore_slot_encrypt(fsl_shw_uco_t *user_ctx, fsl_shw_kso_t *keystore,
+ uint64_t owner_id, uint32_t slot, uint32_t length,
+ uint8_t *destination)
+{
+ (void)user_ctx;
+ (void)keystore;
+ (void)owner_id;
+ (void)slot;
+ (void)length;
+ (void)destination;
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+
+#endif /* FSL_HAVE_SCC2 */
+
+/***** Default keystore implementation **************************************/
+
+#ifdef FSL_HAVE_SCC2
+ fsl_shw_return_t shw_kso_init_data(fsl_shw_uco_t *user_ctx,
+ void **user_data)
+{
+ int retval = FSL_RETURN_ERROR_S;
+ keystore_data_t *keystore_data = NULL;
+ fsl_shw_pco_t *capabilities = fsl_shw_get_capabilities(user_ctx);
+ uint32_t partition_size;
+ uint32_t slot_count;
+ uint32_t keystore_data_size;
+ uint8_t UMID[16] = {
+ 0x42, 0, 0, 0, 0x43, 0, 0, 0, 0x19, 0, 0, 0, 0x59, 0, 0, 0};
+ uint32_t permissions =
+ FSL_PERM_TH_R | FSL_PERM_TH_W | FSL_PERM_HD_R | FSL_PERM_HD_W |
+ FSL_PERM_HD_X;
+
+ /* Look up the size of a partition to see how big to make the keystore */
+ partition_size = fsl_shw_pco_get_spo_size_bytes(capabilities);
+
+ /* Calculate the required size of the keystore data structure, based on the
+ * number of keys that can fit in the partition.
+ */
+ slot_count = partition_size / KEYSTORE_SLOT_SIZE;
+ keystore_data_size =
+ sizeof(keystore_data_t) +
+ slot_count * sizeof(keystore_data_slot_info_t);
+
+#ifdef __KERNEL__
+ keystore_data = os_alloc_memory(keystore_data_size, GFP_KERNEL);
+
+#else
+ keystore_data = malloc(keystore_data_size);
+
+#endif
+ if (keystore_data == NULL) {
+ retval = FSL_RETURN_NO_RESOURCE_S;
+ goto out;
+ }
+
+ /* Clear the memory (effectively clear all key assignments) */
+ memset(keystore_data, 0, keystore_data_size);
+
+ /* Place the slot information structure directly after the keystore data
+ * structure.
+ */
+ keystore_data->slot =
+ (keystore_data_slot_info_t *) (keystore_data + 1);
+ keystore_data->slot_count = slot_count;
+
+ /* Retrieve a secure partition to put the keystore in. */
+ keystore_data->base_address =
+ fsl_shw_smalloc(user_ctx, partition_size, UMID, permissions);
+ if (keystore_data->base_address == NULL) {
+ retval = FSL_RETURN_NO_RESOURCE_S;
+ goto out;
+ }
+ *user_data = keystore_data;
+ retval = FSL_RETURN_OK_S;
+out:if (retval != FSL_RETURN_OK_S) {
+ if (keystore_data != NULL) {
+ if (keystore_data->base_address != NULL)
+ fsl_shw_sfree(NULL,
+ keystore_data->base_address);
+
+#ifdef __KERNEL__
+ os_free_memory(keystore_data);
+
+#else
+ free(keystore_data);
+
+#endif
+ }
+ }
+ return retval;
+}
+void shw_kso_cleanup_data(fsl_shw_uco_t *user_ctx, void **user_data)
+{
+ if (user_data != NULL) {
+ keystore_data_t * keystore_data =
+ (keystore_data_t *) (*user_data);
+ fsl_shw_sfree(user_ctx, keystore_data->base_address);
+
+#ifdef __KERNEL__
+ os_free_memory(*user_data);
+
+#else
+ free(*user_data);
+
+#endif
+ }
+ return;
+}
+
+fsl_shw_return_t shw_slot_verify_access(void *user_data, uint64_t owner_id,
+ uint32_t slot)
+{
+ keystore_data_t * data = user_data;
+ if (data->slot[slot].owner == owner_id) {
+ return FSL_RETURN_OK_S;
+ } else {
+
+#ifdef DIAG_DRV_IF
+ LOG_DIAG_ARGS("Access to slot %i fails.\n", slot);
+
+#endif
+ return FSL_RETURN_AUTH_FAILED_S;
+ }
+}
+
+fsl_shw_return_t shw_slot_alloc(void *user_data, uint32_t size,
+ uint64_t owner_id, uint32_t *slot)
+{
+ keystore_data_t *data = user_data;
+ uint32_t i;
+ if (size > KEYSTORE_SLOT_SIZE)
+ return FSL_RETURN_BAD_KEY_LENGTH_S;
+ for (i = 0; i < data->slot_count; i++) {
+ if (data->slot[i].allocated == 0) {
+ data->slot[i].allocated = 1;
+ data->slot[i].owner = owner_id;
+ (*slot) = i;
+
+#ifdef DIAG_DRV_IF
+ LOG_DIAG_ARGS("Keystore: allocated slot %i. Slot "
+ "address: %p\n",
+ (*slot),
+ data->base_address +
+ (*slot) * KEYSTORE_SLOT_SIZE);
+
+#endif
+ return FSL_RETURN_OK_S;
+ }
+ }
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t shw_slot_dealloc(void *user_data, uint64_t owner_id,
+ uint32_t slot)
+{
+ keystore_data_t * data = user_data;
+ (void)owner_id;
+ (void)slot;
+ if (slot >= data->slot_count)
+ return FSL_RETURN_ERROR_S;
+ if (data->slot[slot].allocated == 1) {
+ /* Forcibly remove the data from the keystore */
+ memset(shw_slot_get_address(user_data, slot), 0,
+ KEYSTORE_SLOT_SIZE);
+ data->slot[slot].allocated = 0;
+ return FSL_RETURN_OK_S;
+ }
+ return FSL_RETURN_ERROR_S;
+}
+
+void *shw_slot_get_address(void *user_data, uint32_t slot)
+{
+ keystore_data_t * data = user_data;
+ if (slot >= data->slot_count)
+ return NULL;
+ return data->base_address + slot * KEYSTORE_SLOT_SIZE;
+}
+
+uint32_t shw_slot_get_base(void *user_data, uint32_t slot)
+{
+ keystore_data_t * data = user_data;
+
+ /* There could potentially be more than one secure partition object
+ * associated with this keystore. For now, there is just one.
+ */
+ (void)slot;
+ return (uint32_t) (data->base_address);
+}
+
+uint32_t shw_slot_get_offset(void *user_data, uint32_t slot)
+{
+ keystore_data_t *data = user_data;
+ if (slot >= data->slot_count)
+ return FSL_RETURN_ERROR_S;
+ return (slot * KEYSTORE_SLOT_SIZE);
+}
+
+uint32_t shw_slot_get_slot_size(void *user_data, uint32_t slot)
+{
+ (void)user_data;
+ (void)slot;
+
+ /* All slots are the same size in the default implementation */
+ return KEYSTORE_SLOT_SIZE;
+}
+
+#else /* FSL_HAVE_SCC2 */
+
+#ifdef __KERNEL__
+ fsl_shw_return_t shw_kso_init_data(fsl_shw_uco_t *user_ctx,
+ void **user_data)
+{
+
+ /* The SCC does its own initialization. All that needs to be done here is
+ * make sure an SCC exists.
+ */
+ *user_data = (void *)0xFEEDFEED;
+ return FSL_RETURN_OK_S;
+}
+void shw_kso_cleanup_data(fsl_shw_uco_t *user_ctx, void **user_data)
+{
+
+ /* The SCC does its own cleanup. */
+ *user_data = NULL;
+ return;
+}
+
+fsl_shw_return_t shw_slot_verify_access(void *user_data, uint64_t owner_id,
+ uint32_t slot)
+{
+
+ /* Zero is used for the size because the newer interface does bounds
+ * checking later.
+ */
+ scc_return_t retval;
+ retval = scc_verify_slot_access(owner_id, slot, 0);
+ if (retval == SCC_RET_OK) {
+ return FSL_RETURN_OK_S;
+ }
+ return FSL_RETURN_AUTH_FAILED_S;
+}
+
+fsl_shw_return_t shw_slot_alloc(void *user_data, uint32_t size,
+ uint64_t owner_id, uint32_t *slot)
+{
+ scc_return_t retval;
+
+#ifdef DIAG_DRV_IF
+ LOG_DIAG_ARGS("key length: %i, handle: %i\n", size, *slot);
+
+#endif
+ retval = scc_alloc_slot(size, owner_id, slot);
+ if (retval == SCC_RET_OK)
+ return FSL_RETURN_OK_S;
+
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t shw_slot_dealloc(void *user_data, uint64_t owner_id,
+ uint32_t slot)
+{
+ scc_return_t retval;
+ retval = scc_dealloc_slot(owner_id, slot);
+ if (retval == SCC_RET_OK)
+ return FSL_RETURN_OK_S;
+
+ return FSL_RETURN_ERROR_S;
+}
+void *shw_slot_get_address(void *user_data, uint32_t slot)
+{
+ uint64_t owner_id = *((uint64_t *) user_data);
+ uint32_t address;
+ uint32_t value_size_bytes;
+ uint32_t slot_size_bytes;
+ scc_return_t scc_ret;
+ scc_ret =
+ scc_get_slot_info(owner_id, slot, &address, &value_size_bytes,
+ &slot_size_bytes);
+ if (scc_ret == SCC_RET_OK) {
+ return (void *)address;
+ }
+ return NULL;
+}
+
+uint32_t shw_slot_get_base(void *user_data, uint32_t slot)
+{
+ return 0;
+}
+
+uint32_t shw_slot_get_offset(void *user_data, uint32_t slot)
+{
+ return 0;
+}
+
+
+/* Return the size of the key slot, in octets */
+uint32_t shw_slot_get_slot_size(void *user_data, uint32_t slot)
+{
+ uint64_t owner_id = *((uint64_t *) user_data);
+ uint32_t address;
+ uint32_t value_size_bytes;
+ uint32_t slot_size_bytes;
+ scc_return_t scc_ret;
+ scc_ret =
+ scc_get_slot_info(owner_id, slot, &address, &value_size_bytes,
+ &slot_size_bytes);
+ if (scc_ret == SCC_RET_OK)
+ return slot_size_bytes;
+ return 0;
+}
+
+
+#endif /* __KERNEL__ */
+
+#endif /* FSL_HAVE_SCC2 */
+
+/*****************************************************************************/
diff --git a/drivers/mxc/security/sahara2/fsl_shw_rand.c b/drivers/mxc/security/sahara2/fsl_shw_rand.c
new file mode 100644
index 000000000000..69999c3272f3
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_rand.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file fsl_shw_rand.c
+ *
+ * This file implements Random Number Generation functions of the FSL SHW API
+ * for Sahara.
+ */
+
+#include "sahara.h"
+#include "sf_util.h"
+
+#ifdef __KERNEL__
+EXPORT_SYMBOL(fsl_shw_get_random);
+#endif
+
+/* REQ-S2LRD-PINTFC-API-BASIC-RNG-002 */
+/*!
+ * Get a random number
+ *
+ *
+ * @param user_ctx
+ * @param length
+ * @param data
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data)
+{
+ SAH_SF_DCLS;
+
+ /* perform a sanity check on the uco */
+ ret = sah_validate_uco(user_ctx);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ header = SAH_HDR_RNG_GENERATE; /* Desc. #18 */
+ DESC_OUT_OUT(header, length, data, 0, NULL);
+
+ SAH_SF_EXECUTE();
+
+ out:
+ SAH_SF_DESC_CLEAN();
+
+ return ret;
+}
+
+#ifdef __KERNEL__
+EXPORT_SYMBOL(fsl_shw_add_entropy);
+#endif
+
+/*!
+ * Add entropy to a random number generator
+
+ * @param user_ctx
+ * @param length
+ * @param data
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_add_entropy(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data)
+{
+ SAH_SF_DCLS;
+
+ /* perform a sanity check on the uco */
+ ret = sah_validate_uco(user_ctx);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ header = SAH_HDR_RNG_GENERATE; /* Desc. #18 */
+
+ /* create descriptor #18. Generate random data */
+ DESC_IN_IN(header, 0, NULL, length, data)
+
+ SAH_SF_EXECUTE();
+
+ out:
+ SAH_SF_DESC_CLEAN();
+
+ return ret;
+}
diff --git a/drivers/mxc/security/sahara2/fsl_shw_sym.c b/drivers/mxc/security/sahara2/fsl_shw_sym.c
new file mode 100644
index 000000000000..133b92dad3c1
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_sym.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file fsl_shw_sym.c
+ *
+ * This file implements Symmetric Cipher functions of the FSL SHW API for
+ * Sahara. This does not include CCM.
+ */
+
+#include "sahara.h"
+#include "fsl_platform.h"
+
+#include "sf_util.h"
+#include "adaptor.h"
+
+#ifdef LINUX_KERNEL
+EXPORT_SYMBOL(fsl_shw_symmetric_encrypt);
+EXPORT_SYMBOL(fsl_shw_symmetric_decrypt);
+#endif
+
+#if defined(NEED_CTR_WORKAROUND)
+/* CTR mode needs block-multiple data in/out */
+#define LENGTH_PATCH (sym_ctx->block_size_bytes)
+#define LENGTH_PATCH_MASK (sym_ctx->block_size_bytes-1)
+#else
+#define LENGTH_PATCH 0
+#define LENGTH_PATCH_MASK 0 /* du not use! */
+#endif
+
+/*!
+ * Block of zeroes which is maximum Symmetric block size, used for
+ * initializing context register, etc.
+ */
+static uint32_t block_zeros[4] = {
+ 0, 0, 0, 0
+};
+
+typedef enum cipher_direction {
+ SYM_DECRYPT,
+ SYM_ENCRYPT
+} cipher_direction_t;
+
+/*!
+ * Create and run the chain for a symmetric-key operation.
+ *
+ * @param user_ctx Who the user is
+ * @param key_info What key is to be used
+ * @param sym_ctx Info details about algorithm
+ * @param encrypt 0 = decrypt, non-zero = encrypt
+ * @param length Number of octets at @a in and @a out
+ * @param in Pointer to input data
+ * @param out Location to store output data
+ *
+ * @return The status of handing chain to driver,
+ * or an earlier argument/flag or allocation
+ * error.
+ */
+static fsl_shw_return_t do_symmetric(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ cipher_direction_t encrypt,
+ uint32_t length,
+ const uint8_t * in, uint8_t * out)
+{
+ SAH_SF_DCLS;
+ uint8_t *sink = NULL;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+ sah_Oct_Str ptr1;
+ uint32_t size1 = sym_ctx->block_size_bytes;
+
+ SAH_SF_USER_CHECK();
+
+ /* Two different sets of chains, depending on algorithm */
+ if (key_info->algorithm == FSL_KEY_ALG_ARC4) {
+ if (sym_ctx->flags & FSL_SYM_CTX_INIT) {
+ /* Desc. #35 w/ARC4 - start from key */
+ header = SAH_HDR_ARC4_SET_MODE_KEY
+ ^ sah_insert_skha_algorithm_arc4;
+
+ DESC_IN_KEY(header, 0, NULL, key_info);
+ } else { /* load SBox */
+ /* Desc. #33 w/ARC4 and NO PERMUTE */
+ header = SAH_HDR_ARC4_SET_MODE_SBOX
+ ^ sah_insert_skha_no_permute
+ ^ sah_insert_skha_algorithm_arc4;
+ DESC_IN_IN(header, 256, sym_ctx->context,
+ 3, sym_ctx->context + 256);
+ } /* load SBox */
+
+ /* Add in-out data descriptor to process the data */
+ if (length != 0) {
+ DESC_IN_OUT(SAH_HDR_SKHA_ENC_DEC, length, in, length,
+ out);
+ }
+
+ /* Operation is done ... save what came out? */
+ if (sym_ctx->flags & FSL_SYM_CTX_SAVE) {
+ /* Desc. #34 - Read SBox, pointers */
+ header = SAH_HDR_ARC4_READ_SBOX;
+ DESC_OUT_OUT(header, 256, sym_ctx->context,
+ 3, sym_ctx->context + 256);
+ }
+ } else { /* not ARC4 */
+ /* Doing 1- or 2- descriptor chain. */
+ /* Desc. #1 and algorithm and mode */
+ header = SAH_HDR_SKHA_SET_MODE_IV_KEY
+ ^ sah_insert_skha_mode[sym_ctx->mode]
+ ^ sah_insert_skha_algorithm[key_info->algorithm];
+
+ /* Honor 'no key parity checking' for DES and TDES */
+ if ((key_info->flags & FSL_SKO_KEY_IGNORE_PARITY) &&
+ ((key_info->algorithm == FSL_KEY_ALG_DES) ||
+ (key_info->algorithm == FSL_KEY_ALG_TDES))) {
+ header ^= sah_insert_skha_no_key_parity;
+ }
+
+ /* Header by default is decrypting, so... */
+ if (encrypt == SYM_ENCRYPT) {
+ header ^= sah_insert_skha_encrypt;
+ }
+
+ if (sym_ctx->mode == FSL_SYM_MODE_CTR) {
+ header ^= sah_insert_skha_modulus[sym_ctx->modulus_exp];
+ }
+
+ if (sym_ctx->mode == FSL_SYM_MODE_ECB) {
+ ptr1 = NULL;
+ size1 = 0;
+ } else if (sym_ctx->flags & FSL_SYM_CTX_INIT) {
+ ptr1 = (uint8_t *) block_zeros;
+ } else {
+ ptr1 = sym_ctx->context;
+ }
+
+ DESC_IN_KEY(header, sym_ctx->block_size_bytes, ptr1, key_info);
+
+ /* Add in-out data descriptor */
+ if (length != 0) {
+ header = SAH_HDR_SKHA_ENC_DEC;
+ if (LENGTH_PATCH && (sym_ctx->mode == FSL_SYM_MODE_CTR)
+ && ((length & LENGTH_PATCH_MASK) != 0)) {
+ sink = DESC_TEMP_ALLOC(LENGTH_PATCH);
+ ret =
+ sah_Create_Link(user_ctx->mem_util, &link1,
+ (uint8_t *) in, length,
+ SAH_USES_LINK_DATA);
+ ret =
+ sah_Append_Link(user_ctx->mem_util, link1,
+ (uint8_t *) sink,
+ LENGTH_PATCH -
+ (length &
+ LENGTH_PATCH_MASK),
+ SAH_USES_LINK_DATA);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ ret =
+ sah_Create_Link(user_ctx->mem_util, &link2,
+ out, length,
+ SAH_USES_LINK_DATA |
+ SAH_OUTPUT_LINK);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ ret = sah_Append_Link(user_ctx->mem_util, link2,
+ sink,
+ LENGTH_PATCH -
+ (length &
+ LENGTH_PATCH_MASK),
+ SAH_USES_LINK_DATA);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ ret =
+ sah_Append_Desc(user_ctx->mem_util,
+ &desc_chain, header, link1,
+ link2);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ link1 = link2 = NULL;
+ } else {
+ DESC_IN_OUT(header, length, in, length, out);
+ }
+ }
+
+ /* Unload any desired context */
+ if (sym_ctx->flags & FSL_SYM_CTX_SAVE) {
+ DESC_OUT_OUT(SAH_HDR_SKHA_READ_CONTEXT_IV, 0, NULL,
+ sym_ctx->block_size_bytes,
+ sym_ctx->context);
+ }
+
+ } /* not ARC4 */
+
+ SAH_SF_EXECUTE();
+
+ out:
+ SAH_SF_DESC_CLEAN();
+ DESC_TEMP_FREE(sink);
+ if (LENGTH_PATCH) {
+ sah_Destroy_Link(user_ctx->mem_util, link1);
+ sah_Destroy_Link(user_ctx->mem_util, link2);
+ }
+
+ return ret;
+}
+
+/* REQ-S2LRD-PINTFC-API-BASIC-SYM-002 */
+/* PINTFC-API-BASIC-SYM-ARC4-001 */
+/* PINTFC-API-BASIC-SYM-ARC4-002 */
+
+/*!
+ * Compute symmetric encryption
+ *
+ *
+ * @param user_ctx
+ * @param key_info
+ * @param sym_ctx
+ * @param length
+ * @param pt
+ * @param ct
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_symmetric_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * pt, uint8_t * ct)
+{
+ fsl_shw_return_t ret;
+
+ ret = do_symmetric(user_ctx, key_info, sym_ctx, SYM_ENCRYPT,
+ length, pt, ct);
+
+ return ret;
+}
+
+/* PINTFC-API-BASIC-SYM-002 */
+/* PINTFC-API-BASIC-SYM-ARC4-001 */
+/* PINTFC-API-BASIC-SYM-ARC4-002 */
+
+/*!
+ * Compute symmetric decryption
+ *
+ *
+ * @param user_ctx
+ * @param key_info
+ * @param sym_ctx
+ * @param length
+ * @param pt
+ * @param ct
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_symmetric_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * ct, uint8_t * pt)
+{
+ fsl_shw_return_t ret;
+
+ ret = do_symmetric(user_ctx, key_info, sym_ctx, SYM_DECRYPT,
+ length, ct, pt);
+
+ return ret;
+}
diff --git a/drivers/mxc/security/sahara2/fsl_shw_user.c b/drivers/mxc/security/sahara2/fsl_shw_user.c
new file mode 100644
index 000000000000..e53b29cfd404
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_user.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file fsl_shw_user.c
+ *
+ * This file implements user and platform capabilities functions of the FSL SHW
+ * API for Sahara
+ */
+#include "sahara.h"
+#include <adaptor.h>
+#include <sf_util.h>
+
+#ifdef __KERNEL__
+EXPORT_SYMBOL(fsl_shw_get_capabilities);
+EXPORT_SYMBOL(fsl_shw_register_user);
+EXPORT_SYMBOL(fsl_shw_deregister_user);
+EXPORT_SYMBOL(fsl_shw_get_results);
+#endif /* __KERNEL__ */
+
+struct cap_t {
+ unsigned populated;
+ union {
+ uint32_t buffer[sizeof(fsl_shw_pco_t)];
+ fsl_shw_pco_t pco;
+ };
+};
+
+static struct cap_t cap = {
+ 0,
+ {}
+};
+
+/* REQ-S2LRD-PINTFC-API-GEN-003 */
+/*!
+ * Determine the hardware security capabilities of this platform.
+ *
+ * Though a user context object is passed into this function, it will always
+ * act in a non-blocking manner.
+ *
+ * @param user_ctx The user context which will be used for the query.
+ *
+ * @return A pointer to the capabilities object.
+ */
+fsl_shw_pco_t *fsl_shw_get_capabilities(fsl_shw_uco_t * user_ctx)
+{
+ fsl_shw_pco_t *retval = NULL;
+
+ if (cap.populated) {
+ retval = &cap.pco;
+ } else {
+ if (get_capabilities(user_ctx, &cap.pco) == FSL_RETURN_OK_S) {
+ cap.populated = 1;
+ retval = &cap.pco;
+ }
+ }
+ return retval;
+}
+
+/* REQ-S2LRD-PINTFC-API-GEN-004 */
+
+/*!
+ * Create an association between the the user and the provider of the API.
+ *
+ * @param user_ctx The user context which will be used for this association.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_register_user(fsl_shw_uco_t * user_ctx)
+{
+ return sah_register(user_ctx);
+}
+
+/* REQ-S2LRD-PINTFC-API-GEN-005 */
+
+/*!
+ * Destroy the association between the the user and the provider of the API.
+ *
+ * @param user_ctx The user context which is no longer needed.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_deregister_user(fsl_shw_uco_t * user_ctx)
+{
+ return sah_deregister(user_ctx);
+}
+
+/* REQ-S2LRD-PINTFC-API-GEN-006 */
+
+/*!
+ * Retrieve results from earlier operations.
+ *
+ * @param user_ctx The user's context.
+ * @param result_size The number of array elements of @a results.
+ * @param[in,out] results Pointer to first of the (array of) locations to
+ * store results.
+ * @param[out] result_count Pointer to store the number of results which
+ * were returned.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_get_results(fsl_shw_uco_t * user_ctx,
+ unsigned result_size,
+ fsl_shw_result_t results[],
+ unsigned *result_count)
+{
+ fsl_shw_return_t status;
+
+ /* perform a sanity check on the uco */
+ status = sah_validate_uco(user_ctx);
+
+ /* if uco appears ok, build structure and pass to get results */
+ if (status == FSL_RETURN_OK_S) {
+ sah_results arg;
+
+ /* if requested is zero, it's done before it started */
+ if (result_size > 0) {
+ arg.requested = result_size;
+ arg.actual = result_count;
+ arg.results = results;
+ /* get the results */
+ status = sah_get_results(&arg, user_ctx);
+ }
+ }
+
+ return status;
+}
diff --git a/drivers/mxc/security/sahara2/fsl_shw_wrap.c b/drivers/mxc/security/sahara2/fsl_shw_wrap.c
new file mode 100644
index 000000000000..9f2e28c7b84d
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_wrap.c
@@ -0,0 +1,967 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file fsl_shw_wrap.c
+ *
+ * This file implements Key-Wrap (Black Key) functions of the FSL SHW API for
+ * Sahara.
+ *
+ * - Ownerid is an 8-byte, user-supplied, value to keep KEY confidential.
+ * - KEY is a 1-32 byte value which starts in SCC RED RAM before
+ * wrapping, and ends up there on unwrap. Length is limited because of
+ * size of SCC1 RAM.
+ * - KEY' is the encrypted KEY
+ * - LEN is a 1-byte (for now) byte-length of KEY
+ * - ALG is a 1-byte value for the algorithm which which the key is
+ * associated. Values are defined by the FSL SHW API
+ * - Ownerid, LEN, and ALG come from the user's "key_info" object, as does the
+ * slot number where KEY already is/will be.
+ * - T is a Nonce
+ * - T' is the encrypted T
+ * - KEK is a Key-Encryption Key for the user's Key
+ * - ICV is the "Integrity Check Value" for the wrapped key
+ * - Black Key is the string of bytes returned as the wrapped key
+<table>
+<tr><TD align="right">BLACK_KEY <TD width="3">=<TD>ICV | T' | LEN | ALG |
+ KEY'</td></tr>
+<tr><td>&nbsp;</td></tr>
+
+<tr><th>To Wrap</th></tr>
+<tr><TD align="right">T</td> <TD width="3">=</td> <TD>RND()<sub>16</sub>
+ </td></tr>
+<tr><TD align="right">KEK</td><TD width="3">=</td><TD>HASH<sub>sha256</sub>(T |
+ Ownerid)<sub>16</sub></td></tr>
+<tr><TD align="right">KEY'<TD width="3">=</td><TD>
+ AES<sub>ctr-enc</sub>(Key=KEK, CTR=0, Data=KEY)</td></tr>
+<tr><TD align="right">ICV</td><TD width="3">=</td><td>HMAC<sub>sha256</sub>
+ (Key=T, Data=Ownerid | LEN | ALG | KEY')<sub>16</sub></td></tr>
+<tr><TD align="right">T'</td><TD width="3">=</td><TD>TDES<sub>cbc-enc</sub>
+ (Key=SLID, IV=Ownerid, Data=T)</td></tr>
+
+<tr><td>&nbsp;</td></tr>
+
+<tr><th>To Unwrap</th></tr>
+<tr><TD align="right">T</td><TD width="3">=</td><TD>TDES<sub>ecb-dec</sub>
+ (Key=SLID, IV=Ownerid, Data=T')</sub></td></tr>
+<tr><TD align="right">ICV</td><TD width="3">=</td><td>HMAC<sub>sha256</sub>
+ (Key=T, Data=Ownerid | LEN | ALG | KEY')<sub>16</sub></td></tr>
+<tr><TD align="right">KEK</td><TD width="3">=</td><td>HASH<sub>sha256</sub>
+ (T | Ownerid)<sub>16</sub></td></tr>
+<tr><TD align="right">KEY<TD width="3">=</td><TD>AES<sub>ctr-dec</sub>
+ (Key=KEK, CTR=0, Data=KEY')</td></tr>
+</table>
+
+ */
+
+#include "sahara.h"
+#include "fsl_platform.h"
+#include "fsl_shw_keystore.h"
+
+#include "sf_util.h"
+#include "adaptor.h"
+
+#if defined(DIAG_SECURITY_FUNC)
+#include <diagnostic.h>
+#endif
+
+#if defined(NEED_CTR_WORKAROUND)
+/* CTR mode needs block-multiple data in/out */
+#define LENGTH_PATCH 16
+#define LENGTH_PATCH_MASK 0xF
+#else
+#define LENGTH_PATCH 4
+#define LENGTH_PATCH_MASK 3
+#endif
+
+#if LENGTH_PATCH
+#define ROUND_LENGTH(len) \
+({ \
+ uint32_t orig_len = len; \
+ uint32_t new_len; \
+ \
+ if ((orig_len & LENGTH_PATCH_MASK) != 0) { \
+ new_len = (orig_len + LENGTH_PATCH \
+ - (orig_len & LENGTH_PATCH_MASK)); \
+ } \
+ else { \
+ new_len = orig_len; \
+ } \
+ new_len; \
+})
+#else
+#define ROUND_LENGTH(len) (len)
+#endif
+
+#ifdef __KERNEL__
+EXPORT_SYMBOL(fsl_shw_establish_key);
+EXPORT_SYMBOL(fsl_shw_extract_key);
+EXPORT_SYMBOL(fsl_shw_release_key);
+EXPORT_SYMBOL(fsl_shw_read_key);
+#endif
+
+#define ICV_LENGTH 16
+#define T_LENGTH 16
+#define KEK_LENGTH 16
+#define LENGTH_LENGTH 1
+#define ALGORITHM_LENGTH 1
+#define FLAGS_LENGTH 1
+
+/* ICV | T' | LEN | ALG | KEY' */
+#define ICV_OFFSET 0
+#define T_PRIME_OFFSET (ICV_OFFSET + ICV_LENGTH)
+#define LENGTH_OFFSET (T_PRIME_OFFSET + T_LENGTH)
+#define ALGORITHM_OFFSET (LENGTH_OFFSET + LENGTH_LENGTH)
+#define FLAGS_OFFSET (ALGORITHM_OFFSET + ALGORITHM_LENGTH)
+#define KEY_PRIME_OFFSET (FLAGS_OFFSET + FLAGS_LENGTH)
+#define FLAGS_SW_KEY 0x01
+
+/*
+ * For testing of the algorithm implementation,, the DO_REPEATABLE_WRAP flag
+ * causes the T_block to go into the T field during a wrap operation. This
+ * will make the black key value repeatable (for a given SCC secret key, or
+ * always if the default key is in use).
+ *
+ * Normally, a random sequence is used.
+ */
+#ifdef DO_REPEATABLE_WRAP
+/*!
+ * Block of zeroes which is maximum Symmetric block size, used for
+ * initializing context register, etc.
+ */
+static uint8_t T_block_[16] = {
+ 0x42, 0, 0, 0x42, 0x42, 0, 0, 0x42,
+ 0x42, 0, 0, 0x42, 0x42, 0, 0, 0x42
+};
+#endif
+
+/*!
+ * Insert descriptors to calculate ICV = HMAC(key=T, data=LEN|ALG|KEY')
+ *
+ * @param user_ctx User's context for this operation
+ * @param desc_chain Descriptor chain to append to
+ * @param t_key_info T's key object
+ * @param black_key Beginning of Black Key region
+ * @param key_length Number of bytes of key' there are in @c black_key
+ * @param[out] hmac Location to store ICV. Will be tagged "USES" so
+ * sf routines will not try to free it.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static inline fsl_shw_return_t create_icv_calc(fsl_shw_uco_t * user_ctx,
+ sah_Head_Desc ** desc_chain,
+ fsl_shw_sko_t * t_key_info,
+ const uint8_t * black_key,
+ uint32_t key_length,
+ uint8_t * hmac)
+{
+ fsl_shw_return_t sah_code;
+ uint32_t header;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+
+ /* Load up T as key for the HMAC */
+ header = (SAH_HDR_MDHA_SET_MODE_MD_KEY /* #6 */
+ ^ sah_insert_mdha_algorithm_sha256
+ ^ sah_insert_mdha_init ^ sah_insert_mdha_hmac ^
+ sah_insert_mdha_pdata ^ sah_insert_mdha_mac_full);
+ sah_code = sah_add_in_key_desc(header, NULL, 0, t_key_info, /* Reference T in RED */
+ user_ctx->mem_util, desc_chain);
+ if (sah_code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Previous step loaded key; Now set up to hash the data */
+ header = SAH_HDR_MDHA_HASH; /* #10 */
+
+ /* Input - start with ownerid */
+ sah_code = sah_Create_Link(user_ctx->mem_util, &link1,
+ (void *)&t_key_info->userid,
+ sizeof(t_key_info->userid),
+ SAH_USES_LINK_DATA);
+ if (sah_code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Still input - Append black-key fields len, alg, key' */
+ sah_code = sah_Append_Link(user_ctx->mem_util, link1,
+ (void *)black_key + LENGTH_OFFSET,
+ (LENGTH_LENGTH
+ + ALGORITHM_LENGTH
+ + key_length), SAH_USES_LINK_DATA);
+
+ if (sah_code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ /* Output - computed ICV/HMAC */
+ sah_code = sah_Create_Link(user_ctx->mem_util, &link2,
+ hmac, ICV_LENGTH,
+ SAH_USES_LINK_DATA | SAH_OUTPUT_LINK);
+ if (sah_code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ sah_code = sah_Append_Desc(user_ctx->mem_util, desc_chain,
+ header, link1, link2);
+
+ out:
+ if (sah_code != FSL_RETURN_OK_S) {
+ (void)sah_Destroy_Link(user_ctx->mem_util, link1);
+ (void)sah_Destroy_Link(user_ctx->mem_util, link2);
+ }
+
+ return sah_code;
+} /* create_icv_calc */
+
+/*!
+ * Perform unwrapping of a black key into a RED slot
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] key_info The information about the key to be which will
+ * be unwrapped... key length, slot info, etc.
+ * @param black_key Encrypted key
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static fsl_shw_return_t unwrap(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ const uint8_t * black_key)
+{
+ SAH_SF_DCLS;
+ uint8_t *hmac = NULL;
+ fsl_shw_sko_t t_key_info;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+ unsigned i;
+ unsigned rounded_key_length;
+ unsigned original_key_length = key_info->key_length;
+
+ hmac = DESC_TEMP_ALLOC(ICV_LENGTH);
+
+ /* Set up key_info for "T" - use same slot as eventual key */
+ fsl_shw_sko_init(&t_key_info, FSL_KEY_ALG_AES);
+ t_key_info.userid = key_info->userid;
+ t_key_info.handle = key_info->handle;
+ t_key_info.flags = key_info->flags;
+ t_key_info.key_length = T_LENGTH;
+ t_key_info.keystore = key_info->keystore;
+
+ /* Validate SW flags to prevent misuse */
+ if ((key_info->flags & FSL_SKO_KEY_SW_KEY)
+ && !(black_key[FLAGS_OFFSET] & FLAGS_SW_KEY)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ /* Compute T = SLID_decrypt(T'); leave in RED slot */
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_decrypt(user_ctx,
+ key_info->userid,
+ t_key_info.handle,
+ T_LENGTH,
+ black_key + T_PRIME_OFFSET);
+
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_decrypt(user_ctx,
+ key_info->keystore,
+ key_info->userid,
+ t_key_info.handle,
+ T_LENGTH,
+ black_key + T_PRIME_OFFSET);
+ }
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Compute ICV = HMAC(T, ownerid | len | alg | key' */
+ ret = create_icv_calc(user_ctx, &desc_chain, &t_key_info,
+ black_key, original_key_length, hmac);
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Creation of sah_Key_Link failed due to bad key"
+ " flag!\n");
+#endif /*DIAG_SECURITY_FUNC */
+ goto out;
+ }
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Validating MAC of wrapped key");
+#endif
+ SAH_SF_EXECUTE();
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ SAH_SF_DESC_CLEAN();
+
+ /* Check computed ICV against value in Black Key */
+ for (i = 0; i < ICV_LENGTH; i++) {
+ if (black_key[ICV_OFFSET + i] != hmac[i]) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("computed ICV fails at offset %i\n", i);
+
+ {
+ char buff[300];
+ int a;
+ for (a = 0; a < ICV_LENGTH; a++)
+ sprintf(&(buff[a * 2]), "%02x",
+ black_key[ICV_OFFSET + a]);
+ buff[a * 2 + 1] = 0;
+ LOG_DIAG_ARGS("black key: %s", buff);
+
+ for (a = 0; a < ICV_LENGTH; a++)
+ sprintf(&(buff[a * 2]), "%02x",
+ hmac[a]);
+ buff[a * 2 + 1] = 0;
+ LOG_DIAG_ARGS("hmac: %s", buff);
+ }
+#endif
+ ret = FSL_RETURN_AUTH_FAILED_S;
+ goto out;
+ }
+ }
+
+ /* This is no longer needed. */
+ DESC_TEMP_FREE(hmac);
+
+ /* Compute KEK = SHA256(T | ownerid). Rewrite slot with value */
+ header = (SAH_HDR_MDHA_SET_MODE_HASH /* #8 */
+ ^ sah_insert_mdha_init
+ ^ sah_insert_mdha_algorithm_sha256 ^ sah_insert_mdha_pdata);
+
+ /* Input - Start with T */
+ ret = sah_Create_Key_Link(user_ctx->mem_util, &link1, &t_key_info);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Still input - append ownerid */
+ ret = sah_Append_Link(user_ctx->mem_util, link1,
+ (void *)&key_info->userid,
+ sizeof(key_info->userid), SAH_USES_LINK_DATA);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Output - KEK goes into RED slot */
+ ret = sah_Create_Key_Link(user_ctx->mem_util, &link2, &t_key_info);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Put the Hash calculation into the chain. */
+ ret = sah_Append_Desc(user_ctx->mem_util, &desc_chain,
+ header, link1, link2);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Compute KEY = AES-decrypt(KEK, KEY') */
+ header = (SAH_HDR_SKHA_SET_MODE_IV_KEY /* #1 */
+ ^ sah_insert_skha_mode_ctr
+ ^ sah_insert_skha_algorithm_aes
+ ^ sah_insert_skha_modulus_128);
+ /* Load KEK in as the key to use */
+ DESC_IN_KEY(header, 0, NULL, &t_key_info);
+
+ rounded_key_length = ROUND_LENGTH(original_key_length);
+ key_info->key_length = rounded_key_length;
+
+ /* Now set up for computation. Result in RED */
+ header = SAH_HDR_SKHA_ENC_DEC; /* #4 */
+ DESC_IN_KEY(header, rounded_key_length, black_key + KEY_PRIME_OFFSET,
+ key_info);
+
+ /* Perform the operation */
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Decrypting key with KEK");
+#endif
+ SAH_SF_EXECUTE();
+
+ out:
+ key_info->key_length = original_key_length;
+ SAH_SF_DESC_CLEAN();
+
+ DESC_TEMP_FREE(hmac);
+
+ /* Erase tracks */
+ t_key_info.userid = 0xdeadbeef;
+ t_key_info.handle = 0xdeadbeef;
+
+ return ret;
+} /* unwrap */
+
+/*!
+ * Perform wrapping of a black key from a RED slot
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] key_info The information about the key to be which will
+ * be wrapped... key length, slot info, etc.
+ * @param black_key Place to store encrypted key
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static fsl_shw_return_t wrap(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info, uint8_t * black_key)
+{
+ SAH_SF_DCLS;
+ unsigned slots_allocated = 0; /* boolean */
+ fsl_shw_sko_t T_key_info; /* for holding T */
+ fsl_shw_sko_t KEK_key_info; /* for holding KEK */
+ unsigned original_key_length = key_info->key_length;
+ unsigned rounded_key_length;
+ sah_Link *link1;
+ sah_Link *link2;
+
+ black_key[LENGTH_OFFSET] = key_info->key_length;
+ black_key[ALGORITHM_OFFSET] = key_info->algorithm;
+
+ memcpy(&T_key_info, key_info, sizeof(T_key_info));
+ fsl_shw_sko_set_key_length(&T_key_info, T_LENGTH);
+ T_key_info.algorithm = FSL_KEY_ALG_HMAC;
+
+ memcpy(&KEK_key_info, &T_key_info, sizeof(KEK_key_info));
+ KEK_key_info.algorithm = FSL_KEY_ALG_AES;
+
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_alloc(user_ctx,
+ T_LENGTH, key_info->userid,
+ &T_key_info.handle);
+
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_alloc(key_info->keystore,
+ T_LENGTH,
+ key_info->userid, &T_key_info.handle);
+ }
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_alloc(user_ctx,
+ KEK_LENGTH, key_info->userid,
+ &KEK_key_info.handle);
+
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_alloc(key_info->keystore,
+ KEK_LENGTH, key_info->userid,
+ &KEK_key_info.handle);
+ }
+
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("do_scc_slot_alloc() failed");
+#endif
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ (void)do_system_keystore_slot_dealloc(user_ctx,
+ key_info->userid, T_key_info.handle);
+
+ } else {
+ /* Key goes in user keystore */
+ (void)keystore_slot_dealloc(key_info->keystore,
+ key_info->userid, T_key_info.handle);
+ }
+ } else {
+ slots_allocated = 1;
+ }
+
+ /* Set up to compute everything except T' ... */
+#ifndef DO_REPEATABLE_WRAP
+ /* Compute T = RND() */
+ header = SAH_HDR_RNG_GENERATE; /* Desc. #18 */
+ DESC_KEY_OUT(header, &T_key_info, 0, NULL);
+#else
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_load(user_ctx,
+ T_key_info.userid,
+ T_key_info.handle, T_block,
+ T_key_info.key_length);
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_load(key_info->keystore,
+ T_key_info.userid,
+ T_key_info.handle,
+ T_block, T_key_info.key_length);
+ }
+
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+#endif
+
+ /* Compute KEK = SHA256(T | Ownerid) */
+ header = (SAH_HDR_MDHA_SET_MODE_HASH /* #8 */
+ ^ sah_insert_mdha_init
+ ^ sah_insert_mdha_algorithm[FSL_HASH_ALG_SHA256]
+ ^ sah_insert_mdha_pdata);
+ /* Input - Start with T */
+ ret = sah_Create_Key_Link(user_ctx->mem_util, &link1, &T_key_info);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ /* Still input - append ownerid */
+ ret = sah_Append_Link(user_ctx->mem_util, link1,
+ (void *)&key_info->userid,
+ sizeof(key_info->userid), SAH_USES_LINK_DATA);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ /* Output - KEK goes into RED slot */
+ ret = sah_Create_Key_Link(user_ctx->mem_util, &link2, &KEK_key_info);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ /* Put the Hash calculation into the chain. */
+ ret = sah_Append_Desc(user_ctx->mem_util, &desc_chain,
+ header, link1, link2);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+#if defined(NEED_CTR_WORKAROUND)
+ rounded_key_length = ROUND_LENGTH(original_key_length);
+ key_info->key_length = rounded_key_length;
+#else
+ rounded_key_length = original_key_length;
+#endif
+ /* Compute KEY' = AES-encrypt(KEK, KEY) */
+ header = (SAH_HDR_SKHA_SET_MODE_IV_KEY /* #1 */
+ ^ sah_insert_skha_mode[FSL_SYM_MODE_CTR]
+ ^ sah_insert_skha_algorithm[FSL_KEY_ALG_AES]
+ ^ sah_insert_skha_modulus[FSL_CTR_MOD_128]);
+ /* Set up KEK as key to use */
+ DESC_IN_KEY(header, 0, NULL, &KEK_key_info);
+ header = SAH_HDR_SKHA_ENC_DEC;
+ DESC_KEY_OUT(header, key_info,
+ key_info->key_length, black_key + KEY_PRIME_OFFSET);
+
+ /* Set up flags info */
+ black_key[FLAGS_OFFSET] = 0;
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ black_key[FLAGS_OFFSET] |= FLAGS_SW_KEY;
+ }
+
+ /* Compute and store ICV into Black Key */
+ ret = create_icv_calc(user_ctx, &desc_chain, &T_key_info,
+ black_key, original_key_length,
+ black_key + ICV_OFFSET);
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Creation of sah_Key_Link failed due to bad key"
+ " flag!\n");
+#endif /*DIAG_SECURITY_FUNC */
+ goto out;
+ }
+
+ /* Now get Sahara to do the work. */
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Encrypting key with KEK");
+#endif
+ SAH_SF_EXECUTE();
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("sah_Descriptor_Chain_Execute() failed");
+#endif
+ goto out;
+ }
+
+ /* Compute T' = SLID_encrypt(T); Result goes to Black Key */
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_encrypt(user_ctx,
+ T_key_info.userid, T_key_info.handle,
+ T_LENGTH, black_key + T_PRIME_OFFSET);
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_encrypt(user_ctx,
+ key_info->keystore,
+ T_key_info.userid,
+ T_key_info.handle,
+ T_LENGTH,
+ black_key + T_PRIME_OFFSET);
+ }
+
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("do_scc_slot_encrypt() failed");
+#endif
+ goto out;
+ }
+
+ out:
+ key_info->key_length = original_key_length;
+
+ SAH_SF_DESC_CLEAN();
+ if (slots_allocated) {
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ (void)do_system_keystore_slot_dealloc(user_ctx,
+ key_info->userid,
+ T_key_info.
+ handle);
+ (void)do_system_keystore_slot_dealloc(user_ctx,
+ key_info->userid,
+ KEK_key_info.
+ handle);
+ } else {
+ /* Key goes in user keystore */
+ (void)keystore_slot_dealloc(key_info->keystore,
+ key_info->userid,
+ T_key_info.handle);
+ (void)keystore_slot_dealloc(key_info->keystore,
+ key_info->userid,
+ KEK_key_info.handle);
+ }
+ }
+
+ return ret;
+} /* wrap */
+
+/*!
+ * Place a key into a protected location for use only by cryptographic
+ * algorithms.
+ *
+ * This only needs to be used to a) unwrap a key, or b) set up a key which
+ * could be wrapped with a later call to #fsl_shw_extract_key(). Normal
+ * cleartext keys can simply be placed into #fsl_shw_sko_t key objects with
+ * #fsl_shw_sko_set_key() and used directly.
+ *
+ * The maximum key size supported for wrapped/unwrapped keys is 32 octets.
+ * (This is the maximum reasonable key length on Sahara - 32 octets for an HMAC
+ * key based on SHA-256.) The key size is determined by the @a key_info. The
+ * expected length of @a key can be determined by
+ * #fsl_shw_sko_calculate_wrapped_size()
+ *
+ * The protected key will not be available for use until this operation
+ * successfully completes.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] key_info The information about the key to be which will
+ * be established. In the create case, the key
+ * length must be set.
+ * @param establish_type How @a key will be interpreted to establish a
+ * key for use.
+ * @param key If @a establish_type is #FSL_KEY_WRAP_UNWRAP,
+ * this is the location of a wrapped key. If
+ * @a establish_type is #FSL_KEY_WRAP_CREATE, this
+ * parameter can be @a NULL. If @a establish_type
+ * is #FSL_KEY_WRAP_ACCEPT, this is the location
+ * of a plaintext key.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_establish_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_key_wrap_t establish_type,
+ const uint8_t * key)
+{
+ SAH_SF_DCLS;
+ unsigned original_key_length = key_info->key_length;
+ unsigned rounded_key_length;
+ unsigned slot_allocated = 0;
+ uint32_t old_flags;
+
+ header = SAH_HDR_RNG_GENERATE; /* Desc. #18 for rand */
+
+ /* TODO: THIS STILL NEEDS TO BE REFACTORED */
+
+ /* Write operations into SCC memory require word-multiple number of
+ * bytes. For ACCEPT and CREATE functions, the key length may need
+ * to be rounded up. Calculate. */
+ if (LENGTH_PATCH && (original_key_length & LENGTH_PATCH_MASK) != 0) {
+ rounded_key_length = original_key_length + LENGTH_PATCH
+ - (original_key_length & LENGTH_PATCH_MASK);
+ } else {
+ rounded_key_length = original_key_length;
+ }
+
+ SAH_SF_USER_CHECK();
+
+ if (key_info->flags & FSL_SKO_KEY_ESTABLISHED) {
+#ifdef DIAG_SECURITY_FUNC
+ ret = FSL_RETURN_BAD_FLAG_S;
+ LOG_DIAG("Key already established\n");
+#endif
+ }
+
+
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_alloc(user_ctx,
+ key_info->key_length,
+ key_info->userid,
+ &(key_info->handle));
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS
+ ("key length: %i, handle: %i, rounded key length: %i",
+ key_info->key_length, key_info->handle,
+ rounded_key_length);
+#endif
+
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_alloc(key_info->keystore,
+ key_info->key_length,
+ key_info->userid,
+ &(key_info->handle));
+ }
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Slot allocation failed\n");
+#endif
+ goto out;
+ }
+ slot_allocated = 1;
+
+ key_info->flags |= FSL_SKO_KEY_ESTABLISHED;
+ switch (establish_type) {
+ case FSL_KEY_WRAP_CREATE:
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Creating random key\n");
+#endif
+ /* Use safe version of key length */
+ key_info->key_length = rounded_key_length;
+ /* Generate descriptor to put random value into */
+ DESC_KEY_OUT(header, key_info, 0, NULL);
+ /* Restore actual, desired key length */
+ key_info->key_length = original_key_length;
+
+ old_flags = user_ctx->flags;
+ /* Now put random value into key */
+ SAH_SF_EXECUTE();
+ /* Restore user's old flag value */
+ user_ctx->flags = old_flags;
+#ifdef DIAG_SECURITY_FUNC
+ if (ret == FSL_RETURN_OK_S) {
+ LOG_DIAG("ret is ok");
+ } else {
+ LOG_DIAG("ret is not ok");
+ }
+#endif
+ break;
+
+ case FSL_KEY_WRAP_ACCEPT:
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Accepting plaintext key\n");
+#endif
+ if (key == NULL) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("ACCEPT: Red Key is NULL");
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+ /* Copy in safe number of bytes of Red key */
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_load(user_ctx,
+ key_info->userid,
+ key_info->handle, key,
+ rounded_key_length);
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_load(key_info->keystore,
+ key_info->userid,
+ key_info->handle, key,
+ key_info->key_length);
+ }
+ break;
+
+ case FSL_KEY_WRAP_UNWRAP:
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Unwrapping wrapped key\n");
+#endif
+ /* For now, disallow non-blocking calls. */
+ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ } else if (key == NULL) {
+ ret = FSL_RETURN_ERROR_S;
+ } else {
+ ret = unwrap(user_ctx, key_info, key);
+ }
+ break;
+
+ default:
+ ret = FSL_RETURN_BAD_FLAG_S;
+ break;
+ } /* switch */
+
+ out:
+ if (slot_allocated && (ret != FSL_RETURN_OK_S)) {
+ fsl_shw_return_t scc_err;
+
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ scc_err = do_system_keystore_slot_dealloc(user_ctx,
+ key_info->userid,
+ key_info->handle);
+ } else {
+ /* Key goes in user keystore */
+ scc_err = keystore_slot_dealloc(key_info->keystore,
+ key_info->userid, key_info->handle);
+ }
+
+ key_info->flags &= ~FSL_SKO_KEY_ESTABLISHED;
+ }
+
+ SAH_SF_DESC_CLEAN();
+
+ return ret;
+} /* fsl_shw_establish_key() */
+
+/*!
+ * Wrap a key and retrieve the wrapped value.
+ *
+ * A wrapped key is a key that has been cryptographically obscured. It is
+ * only able to be used with #fsl_shw_establish_key().
+ *
+ * This function will also release the key (see #fsl_shw_release_key()) so
+ * that it must be re-established before reuse.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The information about the key to be deleted.
+ * @param[out] covered_key The location to store the 48-octet wrapped key.
+ * (This size is based upon the maximum key size
+ * of 32 octets).
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_extract_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ uint8_t * covered_key)
+{
+ SAH_SF_DCLS;
+
+ SAH_SF_USER_CHECK();
+
+ /* For now, only blocking mode calls are supported */
+ if (user_ctx->flags & FSL_UCO_BLOCKING_MODE) {
+ if (key_info->flags & FSL_SKO_KEY_ESTABLISHED) {
+ ret = wrap(user_ctx, key_info, covered_key);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Verify that a SW key info really belongs to a SW key */
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ /* ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;*/
+ }
+
+ /* Need to deallocate on successful extraction */
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_dealloc(user_ctx,
+ key_info->userid, key_info->handle);
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_dealloc(key_info->keystore,
+ key_info->userid, key_info->handle);
+ }
+ /* Mark key not available in the flags */
+ key_info->flags &=
+ ~(FSL_SKO_KEY_ESTABLISHED | FSL_SKO_KEY_PRESENT);
+ }
+ }
+
+out:
+ SAH_SF_DESC_CLEAN();
+
+ return ret;
+}
+
+/*!
+ * De-establish a key so that it can no longer be accessed.
+ *
+ * The key will need to be re-established before it can again be used.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The information about the key to be deleted.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_release_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info)
+{
+ SAH_SF_DCLS;
+
+ SAH_SF_USER_CHECK();
+
+ if (key_info->flags & FSL_SKO_KEY_ESTABLISHED) {
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ do_system_keystore_slot_dealloc(user_ctx,
+ key_info->userid,
+ key_info->handle);
+ } else {
+ /* Key goes in user keystore */
+ keystore_slot_dealloc(key_info->keystore,
+ key_info->userid,
+ key_info->handle);
+ }
+ key_info->flags &= ~(FSL_SKO_KEY_ESTABLISHED |
+ FSL_SKO_KEY_PRESENT);
+ }
+
+out:
+ SAH_SF_DESC_CLEAN();
+
+ return ret;
+}
+
+fsl_shw_return_t fsl_shw_read_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info, uint8_t * key)
+{
+ SAH_SF_DCLS;
+
+ SAH_SF_USER_CHECK();
+
+ if (!(key_info->flags & FSL_SKO_KEY_ESTABLISHED)
+ || !(key_info->flags & FSL_SKO_KEY_SW_KEY)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ if (key_info->keystore == NULL) {
+ /* Key lives in system keystore */
+ ret = do_system_keystore_slot_read(user_ctx,
+ key_info->userid,
+ key_info->handle,
+ key_info->key_length, key);
+ } else {
+ /* Key lives in user keystore */
+ ret = keystore_slot_read(key_info->keystore,
+ key_info->userid,
+ key_info->handle,
+ key_info->key_length, key);
+ }
+
+ out:
+ SAH_SF_DESC_CLEAN();
+
+ return ret;
+}
diff --git a/drivers/mxc/security/sahara2/include/adaptor.h b/drivers/mxc/security/sahara2/include/adaptor.h
new file mode 100644
index 000000000000..4c2d2bd7b688
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/adaptor.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+* @file adaptor.h
+*
+* @brief The Adaptor component provides an interface to the device
+* driver.
+*
+* Intended to be used by the FSL SHW API, this can also be called directly
+*/
+
+#ifndef ADAPTOR_H
+#define ADAPTOR_H
+
+#include <sahara.h>
+
+/*!
+ * Structure passed during user ioctl() call to submit request.
+ */
+typedef struct sah_dar {
+ sah_Desc *desc_addr; /*!< head of descriptor chain */
+ uint32_t uco_flags; /*!< copy of fsl_shw_uco flags field */
+ uint32_t uco_user_ref; /*!< copy of fsl_shw_uco user_ref */
+ uint32_t result; /*!< result of descriptor chain request */
+ struct sah_dar *next; /*!< for driver use */
+} sah_dar_t;
+
+/*!
+ * Structure passed during user ioctl() call to Register a user
+ */
+typedef struct sah_register {
+ uint32_t pool_size; /*!< max number of outstanding requests possible */
+ uint32_t result; /*!< result of registration request */
+} sah_register_t;
+
+/*!
+ * Structure passed during ioctl() call to request SCC operation
+ */
+typedef struct scc_data {
+ uint32_t length; /*!< length of data */
+ uint8_t *in; /*!< input data */
+ uint8_t *out; /*!< output data */
+ unsigned direction; /*!< encrypt or decrypt */
+ fsl_shw_sym_mode_t crypto_mode; /*!< CBC or EBC */
+ uint8_t *init_vector; /*!< initialization vector or NULL */
+} scc_data_t;
+
+/*!
+ * Structure passed during user ioctl() calls to manage stored keys and
+ * stored-key slots.
+ */
+typedef struct scc_slot_t {
+ uint64_t ownerid; /*!< Owner's id to check/set permissions */
+ uint32_t key_length; /*!< Length of key */
+ uint32_t slot; /*!< Slot to operation on, or returned slot
+ number. */
+ uint8_t *key; /*!< User-memory pointer to key value */
+ fsl_shw_return_t code; /*!< API return code from operation */
+} scc_slot_t;
+
+/*
+ * Structure passed during user ioctl() calls to manage data stored in secure
+ * partitions.
+ */
+typedef struct scc_region_t {
+ uint32_t partition_base; /*!< User virtual address of the
+ partition base. */
+ uint32_t offset; /*!< Offset from the start of the
+ partition where the cleartext data
+ is located. */
+ uint32_t length; /*!< Length of the region to be
+ operated on */
+ uint8_t *black_data; /*!< User virtual address of any black
+ (encrypted) data. */
+ fsl_shw_cypher_mode_t cypher_mode; /*!< Cypher mode to use in an encryt/
+ decrypt operation. */
+ uint32_t IV[4]; /*!< Intialization vector to use in an
+ encrypt/decrypt operation. */
+ fsl_shw_return_t code; /*!< API return code from operation */
+} scc_region_t;
+
+/*
+ * Structure passed during user ioctl() calls to manage secure partitions.
+ */
+typedef struct scc_partition_info_t {
+ uint32_t user_base; /**< Userspace pointer to base of partition */
+ uint32_t permissions; /**< Permissions to give the partition (only
+ used in call to _DROP_PERMS) */
+ fsl_shw_partition_status_t status; /*!< Status of the partition */
+} scc_partition_info_t;
+
+fsl_shw_return_t adaptor_Exec_Descriptor_Chain(sah_Head_Desc * dar,
+ fsl_shw_uco_t * uco);
+fsl_shw_return_t sah_get_results(sah_results * arg, fsl_shw_uco_t * uco);
+fsl_shw_return_t sah_register(fsl_shw_uco_t * user_ctx);
+fsl_shw_return_t sah_deregister(fsl_shw_uco_t * user_ctx);
+fsl_shw_return_t get_capabilities(fsl_shw_uco_t * user_ctx,
+ fsl_shw_pco_t *capabilities);
+
+#endif /* ADAPTOR_H */
+
+/* End of adaptor.h */
diff --git a/drivers/mxc/security/sahara2/include/diagnostic.h b/drivers/mxc/security/sahara2/include/diagnostic.h
new file mode 100644
index 000000000000..94db9bfe3a8a
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/diagnostic.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+* @file diagnostic.h
+*
+* @brief Macros for outputting kernel and user space diagnostics.
+*/
+
+#ifndef DIAGNOSTIC_H
+#define DIAGNOSTIC_H
+
+#ifndef __KERNEL__ /* linux flag */
+#include <stdio.h>
+#endif
+#include "fsl_platform.h"
+
+#if defined(FSL_HAVE_SAHARA2) || defined(FSL_HAVE_SAHARA4)
+#define DEV_NAME "sahara"
+#elif defined(FSL_HAVE_RNGA) || defined(FSL_HAVE_RNGB) || \
+ defined(FSL_HAVE_RNGC)
+#define DEV_NAME "shw"
+#endif
+
+/*!
+********************************************************************
+* @brief This macro logs diagnostic messages to stderr.
+*
+* @param diag String that must be logged, char *.
+*
+* @return void
+*
+*/
+//#if defined DIAG_SECURITY_FUNC || defined DIAG_ADAPTOR
+#define LOG_DIAG(diag) \
+({ \
+ const char* fname = strrchr(__FILE__, '/'); \
+ \
+ sah_Log_Diag(fname ? fname+1 : __FILE__, __LINE__, diag); \
+})
+
+#ifdef __KERNEL__
+
+#define LOG_DIAG_ARGS(fmt, ...) \
+({ \
+ const char* fname = strrchr(__FILE__, '/'); \
+ os_printk(KERN_ALERT "%s:%i: " fmt "\n", \
+ fname ? fname+1 : __FILE__, \
+ __LINE__, \
+ __VA_ARGS__); \
+})
+
+#else
+
+#define LOG_DIAG_ARGS(fmt, ...) \
+({ \
+ const char* fname = strrchr(__FILE__, '/'); \
+ printf("%s:%i: " fmt "\n", \
+ fname ? fname+1 : __FILE__, \
+ __LINE__, \
+ __VA_ARGS__); \
+})
+
+#ifndef __KERNEL__
+void sah_Log_Diag(char *source_name, int source_line, char *diag);
+#endif
+#endif /* if define DIAG_SECURITY_FUNC ... */
+
+#ifdef __KERNEL__
+/*!
+********************************************************************
+* @brief This macro logs kernel diagnostic messages to the kernel
+* log.
+*
+* @param diag String that must be logged, char *.
+*
+* @return As for printf()
+*/
+#if 0
+#if defined(DIAG_DRV_IF) || defined(DIAG_DRV_QUEUE) || \
+ defined(DIAG_DRV_STATUS) || defined(DIAG_DRV_INTERRUPT) || \
+ defined(DIAG_MEM) || defined(DIAG_SECURITY_FUNC) || defined(DIAG_ADAPTOR)
+#endif
+#endif
+
+#define LOG_KDIAG_ARGS(fmt, ...) \
+({ \
+ os_printk (KERN_ALERT "%s (%s:%i): " fmt "\n", \
+ DEV_NAME, strrchr(__FILE__, '/')+1, __LINE__, __VA_ARGS__); \
+})
+
+#define LOG_KDIAG(diag) \
+ os_printk (KERN_ALERT "%s (%s:%i): %s\n", \
+ DEV_NAME, strrchr(__FILE__, '/')+1, __LINE__, diag);
+
+#define sah_Log_Diag(n, l, d) \
+ os_printk(KERN_ALERT "%s:%i: %s\n", n, l, d)
+
+#else /* not KERNEL */
+
+#define sah_Log_Diag(n, l, d) \
+ printf("%s:%i: %s\n", n, l, d)
+
+#endif /* __KERNEL__ */
+
+#endif /* DIAGNOSTIC_H */
diff --git a/drivers/mxc/security/sahara2/include/fsl_platform.h b/drivers/mxc/security/sahara2/include/fsl_platform.h
new file mode 100644
index 000000000000..b55bd6cd9a5e
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/fsl_platform.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file fsl_platform.h
+ *
+ * Header file to isolate code which might be platform-dependent
+ */
+
+#ifndef FSL_PLATFORM_H
+#define FSL_PLATFORM_H
+
+#ifdef __KERNEL__
+#include "portable_os.h"
+#endif
+
+#if defined(FSL_PLATFORM_OTHER)
+
+/* Have Makefile or other method of setting FSL_HAVE_* flags */
+
+#elif defined(CONFIG_ARCH_MX3) /* i.MX31 */
+
+#define FSL_HAVE_SCC
+#define FSL_HAVE_RTIC
+#define FSL_HAVE_RNGA
+
+#elif defined(CONFIG_ARCH_MX21)
+
+#define FSL_HAVE_HAC
+#define FSL_HAVE_RNGA
+#define FSL_HAVE_SCC
+
+#elif defined(CONFIG_ARCH_MX25)
+
+#define FSL_HAVE_SCC
+#define FSL_HAVE_RNGB
+#define FSL_HAVE_RTIC3
+#define FSL_HAVE_DRYICE
+
+#elif defined(CONFIG_ARCH_MX27)
+
+#define FSL_HAVE_SAHARA2
+#define SUBMIT_MULTIPLE_DARS
+#define FSL_HAVE_RTIC
+#define FSL_HAVE_SCC
+#define ALLOW_LLO_DESCRIPTORS
+
+#elif defined(CONFIG_ARCH_MX35)
+
+#define FSL_HAVE_SCC
+#define FSL_HAVE_RNGC
+#define FSL_HAVE_RTIC
+
+#elif defined(CONFIG_ARCH_MX37)
+
+#define FSL_HAVE_SCC2
+#define FSL_HAVE_RNGC
+#define FSL_HAVE_RTIC2
+#define FSL_HAVE_SRTC
+
+#elif defined(CONFIG_ARCH_MX5)
+
+#define FSL_HAVE_SCC2
+#define FSL_HAVE_SAHARA4
+#define FSL_HAVE_RTIC3
+#define FSL_HAVE_SRTC
+#define NO_RESEED_WORKAROUND
+#define NEED_CTR_WORKAROUND
+#define USE_S2_CCM_ENCRYPT_CHAIN
+#define USE_S2_CCM_DECRYPT_CHAIN
+#define ALLOW_LLO_DESCRIPTORS
+
+#elif defined(CONFIG_ARCH_MXC91131)
+
+#define FSL_HAVE_SCC
+#define FSL_HAVE_RNGC
+#define FSL_HAVE_HAC
+
+#elif defined(CONFIG_ARCH_MXC91221)
+
+#define FSL_HAVE_SCC
+#define FSL_HAVE_RNGC
+#define FSL_HAVE_RTIC2
+
+#elif defined(CONFIG_ARCH_MXC91231)
+
+#define FSL_HAVE_SAHARA2
+#define FSL_HAVE_RTIC
+#define FSL_HAVE_SCC
+#define NO_OUTPUT_1K_CROSSING
+
+#elif defined(CONFIG_ARCH_MXC91311)
+
+#define FSL_HAVE_SCC
+#define FSL_HAVE_RNGC
+
+#elif defined(CONFIG_ARCH_MXC91314)
+
+#define FSL_HAVE_SCC
+#define FSL_HAVE_SAHAR4
+#define FSL_HAVE_RTIC3
+#define NO_RESEED_WORKAROUND
+#define NEED_CTR_WORKAROUND
+#define USE_S2_CCM_ENCRYPT_CHAIN
+#define USE_S2_CCM_DECRYPT_CHAIN
+#define ALLOW_LLO_DESCRIPTORS
+
+#elif defined(CONFIG_ARCH_MXC91321)
+
+#define FSL_HAVE_SAHARA2
+#define FSL_HAVE_RTIC
+#define FSL_HAVE_SCC
+#define SCC_CLOCK_NOT_GATED
+#define NO_OUTPUT_1K_CROSSING
+
+#elif defined(CONFIG_ARCH_MXC92323)
+
+#define FSL_HAVE_SCC2
+#define FSL_HAVE_SAHARA4
+#define FSL_HAVE_PKHA
+#define FSL_HAVE_RTIC2
+#define NO_1K_CROSSING
+#define NO_RESEED_WORKAROUND
+#define NEED_CTR_WORKAROUND
+#define USE_S2_CCM_ENCRYPT_CHAIN
+#define USE_S2_CCM_DECRYPT_CHAIN
+#define ALLOW_LLO_DESCRIPTORS
+
+
+#elif defined(CONFIG_ARCH_MXC91331)
+
+#define FSL_HAVE_SCC
+#define FSL_HAVE_RNGA
+#define FSL_HAVE_HAC
+#define FSL_HAVE_RTIC
+
+#elif defined(CONFIG_8548)
+
+#define FSL_HAVE_SEC2x
+
+#elif defined(CONFIG_MPC8374)
+
+#define FSL_HAVE_SEC3x
+
+#else
+
+#error UNKNOWN_PLATFORM
+
+#endif /* platform checks */
+
+#endif /* FSL_PLATFORM_H */
diff --git a/drivers/mxc/security/sahara2/include/fsl_shw.h b/drivers/mxc/security/sahara2/include/fsl_shw.h
new file mode 100644
index 000000000000..a52d420c721e
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/fsl_shw.h
@@ -0,0 +1,2515 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*
+ * NOTE TO MAINTAINERS: Although this header file is *the* header file to be
+ * #include'd by FSL SHW programs, it does not itself make any definitions for
+ * the API. Instead, it uses the fsl_platform.h file and / or compiler
+ * environment variables to determine which actual driver header file to
+ * include. This allows different implementations to contain different
+ * implementations of the various objects, macros, etc., or even to change
+ * which functions are macros and which are not.
+ */
+
+/*!
+ * @file fsl_shw.h
+ *
+ * @brief Definition of the Freescale Security Hardware API.
+ *
+ * See @ref index for an overview of the API.
+ */
+
+/*!
+ * @if USE_MAINPAGE
+ * @mainpage Common API for Freescale Security Hardware (FSL SHW API)
+ * @endif
+ *
+ * @section intro_sec Introduction
+ *
+ * This is the interface definition for the Freescale Security Hardware API
+ * (FSL SHW API) for User Mode and Kernel Mode to access Freescale Security
+ * Hardware components for cryptographic acceleration. The API is intended to
+ * provide cross-platform access to security hardware components of Freescale.
+ *
+ * This documentation has not been approved, and should not be taken to
+ * mean anything definite about future direction.
+ *
+ * Some example code is provided to give some idea of usage of this API.
+ *
+ * Note: This first version has been defined around the capabilities of the
+ * Sahara2 cryptographic accelerator, and may be expanded in the future to
+ * provide support for other platforms. The Platform Capabilities Object is
+ * intended as a way to allow programs to adapt to different platforms.
+ *
+ * The i.MX25 is an example of a platform without a SAHARA but yet has
+ * capabilities supported by this API. These include #fsl_shw_get_random() and
+ * #fsl_shw_add_entropy(), and the use of Triple-DES (TDEA) cipher algorithm
+ * (with no checking of key parity supported) in ECB and CBC modes with @ref
+ * sym_sec. See also the @ref di_sec for information on key handling, and @ref
+ * td_sec for detection of Tamper Events. Only the random functions are
+ * available from user space on this platform.
+ *
+ * @section usr_ctx The User Context
+ *
+ * The User Context Object (#fsl_shw_uco_t) controls the interaction between
+ * the user program and the API. It is initialized as part of user
+ * registration (#fsl_shw_register_user()), and is part of every interaction
+ * thereafter.
+ *
+ * @section pf_sec Platform Capabilities
+ *
+ * Since this API is not tied to one specific type of hardware or even one
+ * given version of a given type of hardware, the platform capabilities object
+ * could be used by a portable program to make choices about using software
+ * instead of hardware for certain operations.
+ *
+ * See the #fsl_shw_pco_t, returned by #fsl_shw_get_capabilities().
+ *
+ * @ref pcoops are provided to query its contents.
+ *
+ *
+ * @section sym_sec Symmetric-Key Encryption and Decryption
+ *
+ * Symmetric-Key encryption support is provided for the block cipher algorithms
+ * AES, DES, and Triple DES. Modes supported are #FSL_SYM_MODE_ECB,
+ * #FSL_SYM_MODE_CBC, and #FSL_SYM_MODE_CTR, though not necessarily all modes
+ * for all algorithms. There is also support for the stream cipher algorithm
+ * commonly known as ARC4.
+ *
+ * Encryption and decryption are performed by using the functions
+ * #fsl_shw_symmetric_encrypt() and #fsl_shw_symmetric_decrypt(), respectively.
+ * There are two objects which provide information about the operation of these
+ * functions. They are the #fsl_shw_sko_t, to provide key and algorithm
+ * information; and the #fsl_shw_scco_t, to provide (and store) initial context
+ * or counter value information.
+ *
+ * CCM is not supported by these functions. For information CCM support, see
+ * @ref cmb_sec.
+ *
+ *
+ * @section hash_sec Cryptographic Hashing
+ *
+ * Hashing is performed by fsl_shw_hash(). Control of the function is through
+ * flags in the #fsl_shw_hco_t. The algorithms which are
+ * supported are listed in #fsl_shw_hash_alg_t.
+ *
+ * The hashing function works on octet streams. If a user application needs to
+ * hash a bitstream, it will need to do its own padding of the last block.
+ *
+ *
+ * @section hmac_sec Hashed Message Authentication Codes
+ *
+ * An HMAC is a method of combining a hash and a key so that a message cannot
+ * be faked by a third party.
+ *
+ * The #fsl_shw_hmac() can be used by itself for one-shot or multi-step
+ * operations, or in combination with #fsl_shw_hmac_precompute() to provide the
+ * ability to compute and save the beginning hashes from a key one time, and
+ * then use #fsl_shw_hmac() to calculate an HMAC on each message as it is
+ * processed.
+ *
+ * The maximum key length which is directly supported by this API is 64 octets.
+ * If a longer key size is needed for HMAC, the user will have to hash the key
+ * and present the digest value as the key to be used by the HMAC functions.
+ *
+ *
+ * @section rnd_sec Random Numbers
+ *
+ * Support is available for acquiring random values from a
+ * cryptographically-strong random number generator. See
+ * #fsl_shw_get_random(). The function #fsl_shw_add_entropy() may be used to
+ * add entropy to the random number generator.
+ *
+ *
+ * @section cmb_sec Combined Cipher and Authentication
+ *
+ * Some schemes require that messages be encrypted and that they also have an
+ * authentication code associated with the message. The function
+ * #fsl_shw_gen_encrypt() will generate the authentication code and encrypt the
+ * message.
+ *
+ * Upon receipt of such a message, the message must be decrypted and the
+ * authentication code validated. The function
+ * #fsl_shw_auth_decrypt() will perform these steps.
+ *
+ * Only AES-CCM is supported.
+ *
+ *
+ * @section wrap_sec Wrapped Keys
+ *
+ * On platforms with a Secure Memory, the function #fsl_shw_establish_key() can
+ * be used to place a key into the System Keystore. This key then can be used
+ * directly by the cryptographic hardware. It later then be wrapped
+ * (cryptographically obscured) by #fsl_shw_extract_key() and stored for later
+ * use. If a software key (#FSL_SKO_KEY_SW_KEY) was established, then its
+ * value can be retrieved with a call to #fsl_shw_read_key().
+ *
+ * The wrapping and unwrapping functions provide security against unauthorized
+ * use and detection of tampering.
+ *
+ * The functions can also be used with a User Keystore.
+ *
+ * @section smalloc_sec Secure Memory Allocation
+ *
+ * On platforms with multiple partitions of Secure Memory, the function
+ * #fsl_shw_smalloc() can be used to acquire a partition for private use. The
+ * function #fsl_shw_diminish_perms() can then be used to revoke specific
+ * permissions on the partition, and #fsl_shw_sfree() can be used to release the
+ * partition.
+ *
+ * @section keystore_sec User Keystore
+ *
+ * User Keystore functionality is defined in fsl_shw_keystore.h. See @ref
+ * user_keystore for details. This is not supported on platforms without SCC2.
+ *
+ * @section di_sec Hardware key-select extensions - DryIce
+ *
+ * Some platforms have a component called DryIce which allows the software to
+ * control which key will be used by the secure memory encryption hardware.
+ * The choices are the secret per-chip Fused (IIM) Key, an unknown, hardware-
+ * generated Random Key, a software-written Programmed Key, or the IIM Key in
+ * combination with one of the others. #fsl_shw_pco_check_pk_supported() can
+ * be used to determine whether this feature is available on the platform.
+ * The rest of this section will explain the symmetric ciphering and key
+ * operations which are available on such a platform.
+ *
+ * The function #fsl_shw_sko_init_pf_key() will set up a Secret Key Object to
+ * refer to one of the system's platform keys. All keys which reference a
+ * platform key must use this initialization function, including a user-
+ * provided key value. Keys which are intended for software encryption must
+ * use #fsl_shw_sko_init().
+ *
+ * To change the setting of the Programmed Key of the DryIce module,
+ * #fsl_shw_establish_key() must be called with a platform key object of type
+ * #FSL_SHW_PF_KEY_PRG or #FSL_SHW_PF_KEY_IIM_PRG. The key will be go
+ * into the PK register of DryIce and not to the keystore. Any symmetric
+ * operation which references either #FSL_SHW_PF_KEY_PRG or
+ * #FSL_SHW_PF_KEY_IIM_PRG will use the current PK value (possibly modified by
+ * the secret fused IIM key). Before the Flatform Key can be changed, a call to
+ * #fsl_shw_release_key() or #fsl_shw_extract_key() must be made. Neither
+ * function will change the value in the PK registers, and further ciphering
+ * can take place.
+ *
+ * When #fsl_shw_establish_key() is called to change the PK value, a plaintext
+ * key can be passed in with the #FSL_KEY_WRAP_ACCEPT argument or a previously
+ * wrapped key can be passed in with the #FSL_KEY_WRAP_UNWRAP argument. If
+ * #FSL_KEY_WRAP_CREATE is passed in, then a random value will be loaded into
+ * the PK register. The PK value can be wrapped by a call to
+ * #fsl_shw_extract_key() for later use with the #FSL_KEY_WRAP_UNWRAP argument.
+ *
+ * As an alternative to using only the fused key for @ref wrap_sec,
+ * #fsl_shw_uco_set_wrap_key() can be used to select either the random key or
+ * the random key with the fused key as the key which will be used to protect
+ * the one-time value used to wrap the key. This allows for these
+ * wrapped keys to be dependent upon and therefore unrecoverable after a tamper
+ * event causes the erasure of the DryIce Random Key register.
+ *
+ * The software can request that the hardware generate a (new) Random Key for
+ * DryIce by calling #fsl_shw_gen_random_pf_key().
+ *
+ *
+ * @section td_sec Device Tamper-Detection
+ *
+ * Some platforms have a component which can detect certain types of tampering
+ * with the hardware. #fsl_shw_read_tamper_event() API will allow the
+ * retrieval of the type of event which caused a tamper-detection failure.
+ *
+ */
+
+/*! @defgroup glossary Glossary
+ *
+ * @li @b AES - Advanced Encryption Standard - An NIST-created block cipher
+ * originally knowns as Rijndael.
+ * @li @b ARC4 - ARCFOUR - An S-Box-based OFB mode stream cipher.
+ * @li @b CBC - Cipher-Block Chaining - Each encrypted block is XORed with the
+ * result of the previous block's encryption.
+ * @li @b CCM - A way of combining CBC and CTR to perform cipher and
+ * authentication.
+ * @li @b ciphertext - @a plaintext which has been encrypted in some fashion.
+ * @li @b context - Information on the state of a cryptographic operation,
+ * excluding any key. This could include IV, Counter Value, or SBox.
+ * @li @b CTR - A mode where a counter value is encrypted and then XORed with
+ * the data. After each block, the counter value is incremented.
+ * @li @b DES - Data Encryption Standard - An 8-octet-block cipher.
+ * @li @b ECB - Electronic Codebook - A straight encryption/decryption of the
+ * data.
+ * @li @b hash - A cryptographically strong one-way function performed on data.
+ * @li @b HMAC - Hashed Message Authentication Code - A key-dependent one-way
+ * hash result, used to verify authenticity of a message. The equation
+ * for an HMAC is hash((K + A) || hash((K + B) || msg)), where K is the
+ * key, A is the constant for the outer hash, B is the constant for the
+ * inner hash, and hash is the hashing function (MD5, SHA256, etc).
+ * @li @b IPAD - In an HMAC operation, the context generated by XORing the key
+ * with a constant and then hashing that value as the first block of the
+ * inner hash.
+ * @li @b IV - An "Initial Vector" or @a context for modes like CBC.
+ * @li @b MAC - A Message Authentication Code. HMAC, hashing, and CCM all
+ * produce a MAC.
+ * @li @b mode - A way of using a cryptographic algorithm. See ECB, CBC, etc.
+ * @li @b MD5 - Message Digest 5 - A one-way hash function.
+ * @li @b plaintext - Data which has not been encrypted, or has been decrypted
+ * from @a ciphertext.
+ * @li @b OPAD - In an HMAC operation, the context generated by XORing the key
+ * with a constant and then hashing that value as the first block of the
+ * outer hash.
+ * @li @b SHA - Secure Hash Algorithm - A one-way hash function.
+ * @li @b TDES - AKA @b 3DES - Triple Data Encryption Standard - A method of
+ * using two or three keys and DES to perform three operations (encrypt
+ * decrypt encrypt) to create a new algorithm.
+ * @li @b XOR - Exclusive-OR. A Boolean arithmetic function.
+ * @li @b Wrapped value - A (key) which has been encrypted into an opaque datum
+ * which cannot be unwrapped (decrypted) for use except by an authorized
+ * user. Once created, the key is never visible, but may be used for
+ * other cryptographic operations.
+ */
+
+#ifndef FSL_SHW_H
+#define FSL_SHW_H
+
+/* Set FSL_HAVE_* flags */
+
+#include "fsl_platform.h"
+
+#ifndef API_DOC
+
+#if defined(FSL_HAVE_SAHARA2) || defined(FSL_HAVE_SAHARA4)
+
+#include "sahara.h"
+
+#else
+
+#if defined(FSL_HAVE_RNGA) || defined(FSL_HAVE_RNGB) || defined(FSL_HAVE_RNGC)
+
+#include "rng_driver.h"
+
+#else
+
+#error FSL_SHW_API_platform_not_recognized
+
+#endif
+
+#endif /* HAVE SAHARA */
+
+#else /* API_DOC */
+
+#include <inttypes.h> /* for uint32_t, etc. */
+#include <stdio.h> /* Mainly for definition of NULL !! */
+
+/* These groups will appear in the order in which they are defined. */
+
+/*!
+ * @defgroup strgrp Objects
+ *
+ * These objects are used to pass information into and out of the API. Through
+ * flags and other settings, they control the behavior of the @ref opfuns.
+ *
+ * They are manipulated and queried by use of the various access functions.
+ * There are different sets defined for each object. See @ref objman.
+ */
+
+/*!
+ * @defgroup consgrp Enumerations and other Constants
+ *
+ * This collection of symbols comprise the values which can be passed into
+ * various functions to control how the API will work.
+ */
+
+/*! @defgroup opfuns Operational Functions
+ *
+ * These functions request that the underlying hardware perform cryptographic
+ * operations. They are the heart of the API.
+ */
+
+/****** Organization the Object Operations under one group ! **********/
+/*! @defgroup objman Object-Manipulation Operations
+ *
+ */
+/*! @addtogroup objman
+ @{ */
+/*!
+ * @defgroup pcoops Platform Context Object Operations
+ *
+ * The Platform Context object is "read-only", so only query operations are
+ * provided for it. It is returned by the #fsl_shw_get_capabilities()
+ * function.
+ */
+
+/*! @defgroup ucoops User Context Operations
+ *
+ * These operations should be the only access to the #fsl_shw_uco_t
+ * type/struct, as the internal members of the object are subject to change.
+ * The #fsl_shw_uco_init() function must be called before any other use of the
+ * object.
+ */
+
+/*!
+ * @defgroup rops Result Object Operations
+ *
+ * As the Result Object contains the result of one of the @ref opfuns. The
+ * manipulations provided are query-only. No initialization is needed for this
+ * object.
+ */
+
+/*!
+ * @defgroup skoops Secret Key Object Operations
+ *
+ * These operations should be the only access to the #fsl_shw_sko_t
+ * type/struct, as the internal members of that object are subject to change.
+ */
+
+/*!
+ * @defgroup ksoops Keystore Object Operations
+ *
+ * These operations should be the only access to the #fsl_shw_kso_t
+ * type/struct, as the internal members of that object are subject to change.
+ */
+
+/*!
+ * @defgroup hcops Hash Context Object Operations
+ *
+ * These operations should be the only access to the #fsl_shw_hco_t
+ * type/struct, as the internal members of that object are subject to change.
+ */
+
+/*!
+ * @defgroup hmcops HMAC Context Object Operations
+ *
+ * These operations should be the only access to the #fsl_shw_hmco_t
+ * type/struct, as the internal members of that object are subject to change.
+ */
+
+/*!
+ * @defgroup sccops Symmetric Cipher Context Operations
+ *
+ * These operations should be the only access to the #fsl_shw_scco_t
+ * type/struct, as the internal members of that object are subject to change
+ */
+
+/*! @defgroup accoops Authentication-Cipher Context Object Operations
+ *
+ * These functions operate on a #fsl_shw_acco_t. Their purpose is to set
+ * flags, fields, etc., in order to control the operation of
+ * #fsl_shw_gen_encrypt() and #fsl_shw_auth_decrypt().
+ */
+
+ /* @} *//************ END GROUPING of Object Manipulations *****************/
+
+/*! @defgroup miscfuns Miscellaneous Functions
+ *
+ * These functions are neither @ref opfuns nor @ref objman. Their behavior
+ * does not depend upon the flags in the #fsl_shw_uco_t, yet they may involve
+ * more interaction with the library and the kernel than simply querying an
+ * object.
+ */
+
+/******************************************************************************
+ * Enumerations
+ *****************************************************************************/
+/*! @addtogroup consgrp
+ @{ */
+
+/*!
+ * Flags for the state of the User Context Object (#fsl_shw_uco_t).
+ *
+ * These flags describe how the @ref opfuns will operate.
+ */
+typedef enum fsl_shw_user_ctx_flags_t {
+ /*!
+ * API will block the caller until operation completes. The result will be
+ * available in the return code. If this is not set, user will have to get
+ * results using #fsl_shw_get_results().
+ */
+ FSL_UCO_BLOCKING_MODE,
+ /*!
+ * User wants callback (at the function specified with
+ * #fsl_shw_uco_set_callback()) when the operation completes. This flag is
+ * valid only if #FSL_UCO_BLOCKING_MODE is not set.
+ */
+ FSL_UCO_CALLBACK_MODE,
+ /*! Do not free descriptor chain after driver (adaptor) finishes */
+ FSL_UCO_SAVE_DESC_CHAIN,
+ /*!
+ * User has made at least one request with callbacks requested, so API is
+ * ready to handle others.
+ */
+ FSL_UCO_CALLBACK_SETUP_COMPLETE,
+ /*!
+ * (virtual) pointer to descriptor chain is completely linked with physical
+ * (DMA) addresses, ready for the hardware. This flag should not be used
+ * by FSL SHW API programs.
+ */
+ FSL_UCO_CHAIN_PREPHYSICALIZED,
+ /*!
+ * The user has changed the context but the changes have not been copied to
+ * the kernel driver.
+ */
+ FSL_UCO_CONTEXT_CHANGED,
+ /*! Internal Use. This context belongs to a user-mode API user. */
+ FSL_UCO_USERMODE_USER,
+} fsl_shw_user_ctx_flags_t;
+
+/*!
+ * Return code for FSL_SHW library.
+ *
+ * These codes may be returned from a function call. In non-blocking mode,
+ * they will appear as the status in a Result Object.
+ */
+typedef enum fsl_shw_return_t {
+ /*!
+ * No error. As a function return code in Non-blocking mode, this may
+ * simply mean that the operation was accepted for eventual execution.
+ */
+ FSL_RETURN_OK_S = 0,
+ /*! Failure for non-specific reason. */
+ FSL_RETURN_ERROR_S,
+ /*!
+ * Operation failed because some resource was not able to be allocated.
+ */
+ FSL_RETURN_NO_RESOURCE_S,
+ /*! Crypto algorithm unrecognized or improper. */
+ FSL_RETURN_BAD_ALGORITHM_S,
+ /*! Crypto mode unrecognized or improper. */
+ FSL_RETURN_BAD_MODE_S,
+ /*! Flag setting unrecognized or inconsistent. */
+ FSL_RETURN_BAD_FLAG_S,
+ /*! Improper or unsupported key length for algorithm. */
+ FSL_RETURN_BAD_KEY_LENGTH_S,
+ /*! Improper parity in a (DES, TDES) key. */
+ FSL_RETURN_BAD_KEY_PARITY_S,
+ /*!
+ * Improper or unsupported data length for algorithm or internal buffer.
+ */
+ FSL_RETURN_BAD_DATA_LENGTH_S,
+ /*! Authentication / Integrity Check code check failed. */
+ FSL_RETURN_AUTH_FAILED_S,
+ /*! A memory error occurred. */
+ FSL_RETURN_MEMORY_ERROR_S,
+ /*! An error internal to the hardware occurred. */
+ FSL_RETURN_INTERNAL_ERROR_S,
+ /*! ECC detected Point at Infinity */
+ FSL_RETURN_POINT_AT_INFINITY_S,
+ /*! ECC detected No Point at Infinity */
+ FSL_RETURN_POINT_NOT_AT_INFINITY_S,
+ /*! GCD is One */
+ FSL_RETURN_GCD_IS_ONE_S,
+ /*! GCD is not One */
+ FSL_RETURN_GCD_IS_NOT_ONE_S,
+ /*! Candidate is Prime */
+ FSL_RETURN_PRIME_S,
+ /*! Candidate is not Prime */
+ FSL_RETURN_NOT_PRIME_S,
+ /*! N register loaded improperly with even value */
+ FSL_RETURN_EVEN_MODULUS_ERROR_S,
+ /*! Divisor is zero. */
+ FSL_RETURN_DIVIDE_BY_ZERO_ERROR_S,
+ /*! Bad Exponent or Scalar value for Point Multiply */
+ FSL_RETURN_BAD_EXPONENT_ERROR_S,
+ /*! RNG hardware problem. */
+ FSL_RETURN_OSCILLATOR_ERROR_S,
+ /*! RNG hardware problem. */
+ FSL_RETURN_STATISTICS_ERROR_S,
+} fsl_shw_return_t;
+
+/*!
+ * Algorithm Identifier.
+ *
+ * Selection of algorithm will determine how large the block size of the
+ * algorithm is. Context size is the same length unless otherwise specified.
+ * Selection of algorithm also affects the allowable key length.
+ */
+typedef enum fsl_shw_key_alg_t {
+ FSL_KEY_ALG_HMAC, /*!< Key will be used to perform an HMAC. Key
+ size is 1 to 64 octets. Block size is 64
+ octets. */
+ FSL_KEY_ALG_AES, /*!< Advanced Encryption Standard (Rijndael).
+ Block size is 16 octets. Key size is 16
+ octets. (The single choice of key size is a
+ Sahara platform limitation.) */
+ FSL_KEY_ALG_DES, /*!< Data Encryption Standard. Block size is
+ 8 octets. Key size is 8 octets. */
+ FSL_KEY_ALG_TDES, /*!< 2- or 3-key Triple DES. Block size is 8
+ octets. Key size is 16 octets for 2-key
+ Triple DES, and 24 octets for 3-key. */
+ FSL_KEY_ALG_ARC4 /*!< ARC4. No block size. Context size is 259
+ octets. Allowed key size is 1-16 octets.
+ (The choices for key size are a Sahara
+ platform limitation.) */
+} fsl_shw_key_alg_t;
+
+/*!
+ * Mode selector for Symmetric Ciphers.
+ *
+ * The selection of mode determines how a cryptographic algorithm will be
+ * used to process the plaintext or ciphertext.
+ *
+ * For all modes which are run block-by-block (that is, all but
+ * #FSL_SYM_MODE_STREAM), any partial operations must be performed on a text
+ * length which is multiple of the block size. Except for #FSL_SYM_MODE_CTR,
+ * these block-by-block algorithms must also be passed a total number of octets
+ * which is a multiple of the block size.
+ *
+ * In modes which require that the total number of octets of data be a multiple
+ * of the block size (#FSL_SYM_MODE_ECB and #FSL_SYM_MODE_CBC), and the user
+ * has a total number of octets which are not a multiple of the block size, the
+ * user must perform any necessary padding to get to the correct data length.
+ */
+typedef enum fsl_shw_sym_mode_t {
+ /*!
+ * Stream. There is no associated block size. Any request to process data
+ * may be of any length. This mode is only for ARC4 operations, and is
+ * also the only mode used for ARC4.
+ */
+ FSL_SYM_MODE_STREAM,
+
+ /*!
+ * Electronic Codebook. Each block of data is encrypted/decrypted. The
+ * length of the data stream must be a multiple of the block size. This
+ * mode may be used for DES, 3DES, and AES. The block size is determined
+ * by the algorithm.
+ */
+ FSL_SYM_MODE_ECB,
+ /*!
+ * Cipher-Block Chaining. Each block of data is encrypted/decrypted and
+ * then "chained" with the previous block by an XOR function. Requires
+ * context to start the XOR (previous block). This mode may be used for
+ * DES, 3DES, and AES. The block size is determined by the algorithm.
+ */
+ FSL_SYM_MODE_CBC,
+ /*!
+ * Counter. The counter is encrypted, then XORed with a block of data.
+ * The counter is then incremented (using modulus arithmetic) for the next
+ * block. The final operation may be non-multiple of block size. This mode
+ * may be used for AES. The block size is determined by the algorithm.
+ */
+ FSL_SYM_MODE_CTR,
+} fsl_shw_sym_mode_t;
+
+/*!
+ * Algorithm selector for Cryptographic Hash functions.
+ *
+ * Selection of algorithm determines how large the context and digest will be.
+ * Context is the same size as the digest (resulting hash), unless otherwise
+ * specified.
+ */
+typedef enum fsl_shw_hash_alg_t {
+ FSL_HASH_ALG_MD5, /*!< MD5 algorithm. Digest is 16 octets. */
+ FSL_HASH_ALG_SHA1, /*!< SHA-1 (aka SHA or SHA-160) algorithm.
+ Digest is 20 octets. */
+ FSL_HASH_ALG_SHA224, /*!< SHA-224 algorithm. Digest is 28 octets,
+ though context is 32 octets. */
+ FSL_HASH_ALG_SHA256 /*!< SHA-256 algorithm. Digest is 32
+ octets. */
+} fsl_shw_hash_alg_t;
+
+/*!
+ * The type of Authentication-Cipher function which will be performed.
+ */
+typedef enum fsl_shw_acc_mode_t {
+ /*!
+ * CBC-MAC for Counter. Requires context and modulus. Final operation may
+ * be non-multiple of block size. This mode may be used for AES.
+ */
+ FSL_ACC_MODE_CCM,
+ /*!
+ * SSL mode. Not supported. Combines HMAC and encrypt (or decrypt).
+ * Needs one key object for encryption, another for the HMAC. The usual
+ * hashing and symmetric encryption algorithms are supported.
+ */
+ FSL_ACC_MODE_SSL,
+} fsl_shw_acc_mode_t;
+
+/*!
+ * The operation which controls the behavior of #fsl_shw_establish_key().
+ *
+ * These values are passed to #fsl_shw_establish_key().
+ */
+typedef enum fsl_shw_key_wrap_t {
+ FSL_KEY_WRAP_CREATE, /*!< Generate a key from random values. */
+ FSL_KEY_WRAP_ACCEPT, /*!< Use the provided clear key. */
+ FSL_KEY_WRAP_UNWRAP /*!< Unwrap a previously wrapped key. */
+} fsl_shw_key_wrap_t;
+
+/* REQ-S2LRD-PINTFC-COA-HCO-001 */
+/*!
+ * Flags which control a Hash operation.
+ *
+ * These may be combined by ORing them together. See #fsl_shw_hco_set_flags()
+ * and #fsl_shw_hco_clear_flags().
+ */
+typedef enum fsl_shw_hash_ctx_flags_t {
+ FSL_HASH_FLAGS_INIT = 1, /*!< Context is empty. Hash is started
+ from scratch, with a message-processed
+ count of zero. */
+ FSL_HASH_FLAGS_SAVE = 2, /*!< Retrieve context from hardware after
+ hashing. If used with the
+ #FSL_HASH_FLAGS_FINALIZE flag, the final
+ digest value will be saved in the
+ object. */
+ FSL_HASH_FLAGS_LOAD = 4, /*!< Place context into hardware before
+ hashing. */
+ FSL_HASH_FLAGS_FINALIZE = 8, /*!< PAD message and perform final digest
+ operation. If user message is
+ pre-padded, this flag should not be
+ used. */
+} fsl_shw_hash_ctx_flags_t;
+
+/*!
+ * Flags which control an HMAC operation.
+ *
+ * These may be combined by ORing them together. See #fsl_shw_hmco_set_flags()
+ * and #fsl_shw_hmco_clear_flags().
+ */
+typedef enum fsl_shw_hmac_ctx_flags_t {
+ FSL_HMAC_FLAGS_INIT = 1, /*!< Message context is empty. HMAC is
+ started from scratch (with key) or from
+ precompute of inner hash, depending on
+ whether
+ #FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT is
+ set. */
+ FSL_HMAC_FLAGS_SAVE = 2, /*!< Retrieve ongoing context from hardware
+ after hashing. If used with the
+ #FSL_HMAC_FLAGS_FINALIZE flag, the final
+ digest value (HMAC) will be saved in the
+ object. */
+ FSL_HMAC_FLAGS_LOAD = 4, /*!< Place ongoing context into hardware
+ before hashing. */
+ FSL_HMAC_FLAGS_FINALIZE = 8, /*!< PAD message and perform final HMAC
+ operations of inner and outer hashes. */
+ FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT = 16 /*!< This means that the context
+ contains precomputed inner and outer
+ hash values. */
+} fsl_shw_hmac_ctx_flags_t;
+
+/*!
+ * Flags to control use of the #fsl_shw_scco_t.
+ *
+ * These may be ORed together to get the desired effect.
+ * See #fsl_shw_scco_set_flags() and #fsl_shw_scco_clear_flags()
+ */
+typedef enum fsl_shw_sym_ctx_flags_t {
+ /*!
+ * Context is empty. In ARC4, this means that the S-Box needs to be
+ * generated from the key. In #FSL_SYM_MODE_CBC mode, this allows an IV of
+ * zero to be specified. In #FSL_SYM_MODE_CTR mode, it means that an
+ * initial CTR value of zero is desired.
+ */
+ FSL_SYM_CTX_INIT = 1,
+ /*!
+ * Load context from object into hardware before running cipher. In
+ * #FSL_SYM_MODE_CTR mode, this would refer to the Counter Value.
+ */
+ FSL_SYM_CTX_LOAD = 2,
+ /*!
+ * Save context from hardware into object after running cipher. In
+ * #FSL_SYM_MODE_CTR mode, this would refer to the Counter Value.
+ */
+ FSL_SYM_CTX_SAVE = 4,
+ /*!
+ * Context (SBox) is to be unwrapped and wrapped on each use.
+ * This flag is unsupported.
+ * */
+ FSL_SYM_CTX_PROTECT = 8,
+} fsl_shw_sym_ctx_flags_t;
+
+/*!
+ * Flags which describe the state of the #fsl_shw_sko_t.
+ *
+ * These may be ORed together to get the desired effect.
+ * See #fsl_shw_sko_set_flags() and #fsl_shw_sko_clear_flags()
+ */
+typedef enum fsl_shw_key_flags_t {
+ FSL_SKO_KEY_IGNORE_PARITY = 1, /*!< If algorithm is DES or 3DES, do not
+ validate the key parity bits. */
+ FSL_SKO_KEY_PRESENT = 2, /*!< Clear key is present in the object. */
+ FSL_SKO_KEY_ESTABLISHED = 4, /*!< Key has been established for use. This
+ feature is not available for all
+ platforms, nor for all algorithms and
+ modes. */
+ FSL_SKO_KEY_SW_KEY = 8, /*!< This key is for software use, and can
+ be copied out of a keystore by its owner.
+ The default is that they key is available
+ only for hardware (or security driver)
+ use. */
+} fsl_shw_key_flags_t;
+
+/*!
+ * Type of value which is associated with an established key.
+ */
+typedef uint64_t key_userid_t;
+
+/*!
+ * Flags which describe the state of the #fsl_shw_acco_t.
+ *
+ * The @a FSL_ACCO_CTX_INIT and @a FSL_ACCO_CTX_FINALIZE flags, when used
+ * together, provide for a one-shot operation.
+ */
+typedef enum fsl_shw_auth_ctx_flags_t {
+ FSL_ACCO_CTX_INIT = 1, /*!< Initialize Context(s) */
+ FSL_ACCO_CTX_LOAD = 2, /*!< Load intermediate context(s).
+ This flag is unsupported. */
+ FSL_ACCO_CTX_SAVE = 4, /*!< Save intermediate context(s).
+ This flag is unsupported. */
+ FSL_ACCO_CTX_FINALIZE = 8, /*!< Create MAC during this operation. */
+ FSL_ACCO_NIST_CCM = 16, /*!< Formatting of CCM input data is
+ performed by calls to
+ #fsl_shw_ccm_nist_format_ctr_and_iv() and
+ #fsl_shw_ccm_nist_update_ctr_and_iv(). */
+} fsl_shw_auth_ctx_flags_t;
+
+/*!
+ * Modulus Selector for CTR modes.
+ *
+ * The incrementing of the Counter value may be modified by a modulus. If no
+ * modulus is needed or desired for AES, use #FSL_CTR_MOD_128.
+ */
+typedef enum fsl_shw_ctr_mod_t {
+ FSL_CTR_MOD_8, /*!< Run counter with modulus of 2^8. */
+ FSL_CTR_MOD_16, /*!< Run counter with modulus of 2^16. */
+ FSL_CTR_MOD_24, /*!< Run counter with modulus of 2^24. */
+ FSL_CTR_MOD_32, /*!< Run counter with modulus of 2^32. */
+ FSL_CTR_MOD_40, /*!< Run counter with modulus of 2^40. */
+ FSL_CTR_MOD_48, /*!< Run counter with modulus of 2^48. */
+ FSL_CTR_MOD_56, /*!< Run counter with modulus of 2^56. */
+ FSL_CTR_MOD_64, /*!< Run counter with modulus of 2^64. */
+ FSL_CTR_MOD_72, /*!< Run counter with modulus of 2^72. */
+ FSL_CTR_MOD_80, /*!< Run counter with modulus of 2^80. */
+ FSL_CTR_MOD_88, /*!< Run counter with modulus of 2^88. */
+ FSL_CTR_MOD_96, /*!< Run counter with modulus of 2^96. */
+ FSL_CTR_MOD_104, /*!< Run counter with modulus of 2^104. */
+ FSL_CTR_MOD_112, /*!< Run counter with modulus of 2^112. */
+ FSL_CTR_MOD_120, /*!< Run counter with modulus of 2^120. */
+ FSL_CTR_MOD_128 /*!< Run counter with modulus of 2^128. */
+} fsl_shw_ctr_mod_t;
+
+/*!
+ * Permissions flags for Secure Partitions
+ *
+ * They currently map directly to the SCC2 hardware values, but this is not
+ * guarinteed behavior.
+ */
+typedef enum fsl_shw_permission_t {
+/*! SCM Access Permission: Do not zeroize/deallocate partition on SMN Fail state */
+ FSL_PERM_NO_ZEROIZE,
+/*! SCM Access Permission: Enforce trusted key read in */
+ FSL_PERM_TRUSTED_KEY_READ,
+/*! SCM Access Permission: Ignore Supervisor/User mode in permission determination */
+ FSL_PERM_HD_S,
+/*! SCM Access Permission: Allow Read Access to Host Domain */
+ FSL_PERM_HD_R,
+/*! SCM Access Permission: Allow Write Access to Host Domain */
+ FSL_PERM_HD_W,
+/*! SCM Access Permission: Allow Execute Access to Host Domain */
+ FSL_PERM_HD_X,
+/*! SCM Access Permission: Allow Read Access to Trusted Host Domain */
+ FSL_PERM_TH_R,
+/*! SCM Access Permission: Allow Write Access to Trusted Host Domain */
+ FSL_PERM_TH_W,
+/*! SCM Access Permission: Allow Read Access to Other/World Domain */
+ FSL_PERM_OT_R,
+/*! SCM Access Permission: Allow Write Access to Other/World Domain */
+ FSL_PERM_OT_W,
+/*! SCM Access Permission: Allow Execute Access to Other/World Domain */
+ FSL_PERM_OT_X,
+} fsl_shw_permission_t;
+
+/*!
+ * Select the cypher mode to use for partition cover/uncover operations.
+ *
+ * They currently map directly to the values used in the SCC2 driver, but this
+ * is not guarinteed behavior.
+ */
+typedef enum fsl_shw_cypher_mode_t {
+ FSL_SHW_CYPHER_MODE_ECB, /*!< ECB mode */
+ FSL_SHW_CYPHER_MODE_CBC, /*!< CBC mode */
+} fsl_shw_cypher_mode_t;
+
+/*!
+ * Which platform key should be presented for cryptographic use.
+ */
+typedef enum fsl_shw_pf_key_t {
+ FSL_SHW_PF_KEY_IIM, /*!< Present fused IIM key */
+ FSL_SHW_PF_KEY_PRG, /*!< Present Program key */
+ FSL_SHW_PF_KEY_IIM_PRG, /*!< Present IIM ^ Program key */
+ FSL_SHW_PF_KEY_IIM_RND, /*!< Present Random key */
+ FSL_SHW_PF_KEY_RND, /*!< Present IIM ^ Random key */
+} fsl_shw_pf_key_t;
+
+/*!
+ * The various security tamper events
+ */
+typedef enum fsl_shw_tamper_t {
+ FSL_SHW_TAMPER_NONE, /*!< No error detected */
+ FSL_SHW_TAMPER_WTD, /*!< wire-mesh tampering det */
+ FSL_SHW_TAMPER_ETBD, /*!< ext tampering det: input B */
+ FSL_SHW_TAMPER_ETAD, /*!< ext tampering det: input A */
+ FSL_SHW_TAMPER_EBD, /*!< external boot detected */
+ FSL_SHW_TAMPER_SAD, /*!< security alarm detected */
+ FSL_SHW_TAMPER_TTD, /*!< temperature tampering det */
+ FSL_SHW_TAMPER_CTD, /*!< clock tampering det */
+ FSL_SHW_TAMPER_VTD, /*!< voltage tampering det */
+ FSL_SHW_TAMPER_MCO, /*!< monotonic counter overflow */
+ FSL_SHW_TAMPER_TCO, /*!< time counter overflow */
+} fsl_shw_tamper_t;
+
+/*! @} *//* consgrp */
+
+/******************************************************************************
+ * Data Structures
+ *****************************************************************************/
+/*! @addtogroup strgrp
+ @{ */
+
+/* REQ-S2LRD-PINTFC-COA-IBO-001 */
+/*!
+ * Application Initialization Object
+ *
+ * This object, the operations on it, and its interaction with the driver are
+ * TBD.
+ */
+typedef struct fsl_sho_ibo_t {
+} fsl_sho_ibo_t;
+
+/* REQ-S2LRD-PINTFC-COA-UCO-001 */
+/*!
+ * User Context Object
+ *
+ * This object must be initialized by a call to #fsl_shw_uco_init(). It must
+ * then be passed to #fsl_shw_register_user() before it can be used in any
+ * calls besides those in @ref ucoops.
+ *
+ * It contains the user's configuration for the API, for instance whether an
+ * operation should block, or instead should call back the user upon completion
+ * of the operation.
+ *
+ * See @ref ucoops for further information.
+ */
+typedef struct fsl_shw_uco_t { /* fsl_shw_user_context_object */
+} fsl_shw_uco_t;
+
+/* REQ-S2LRD-PINTFC-API-GEN-006 ?? */
+/*!
+ * Result Object
+ *
+ * This object will contain success and failure information about a specific
+ * cryptographic request which has been made.
+ *
+ * No direct access to its members should be made by programs. Instead, the
+ * object should be manipulated using the provided functions. See @ref rops.
+ */
+typedef struct fsl_shw_result_t { /* fsl_shw_result */
+} fsl_shw_result_t;
+
+/*!
+ * Keystore Object
+ *
+ * This object holds the context of a user keystore, including the functions
+ * that define the interface and pointers to where the key data is stored. The
+ * user must supply a set of functions to handle keystore management, including
+ * slot allocation, deallocation, etc. A default keystore manager is provided
+ * as part of the API.
+ *
+ * No direct access to its members should be made by programs. Instead, the
+ * object should be manipulated using the provided functions. See @ref ksoops.
+ */
+typedef struct fsl_shw_kso_t { /* fsl_shw_keystore_object */
+} fsl_shw_kso_t;
+
+/* REQ-S2LRD-PINTFC-COA-SKO-001 */
+/*!
+ * Secret Key Object
+ *
+ * This object contains a key for a cryptographic operation, and information
+ * about its current state, its intended usage, etc. It may instead contain
+ * information about a protected key, or an indication to use a platform-
+ * specific secret key.
+ *
+ * No direct access to its members should be made by programs. Instead, the
+ * object should be manipulated using the provided functions. See @ref skoops.
+ */
+typedef struct fsl_shw_sko_t { /* fsl_shw_secret_key_object */
+} fsl_shw_sko_t;
+
+/* REQ-S2LRD-PINTFC-COA-CO-001 */
+/*!
+ * Platform Capabilities Object
+ *
+ * This object will contain information about the cryptographic features of the
+ * platform which the program is running on.
+ *
+ * No direct access to its members should be made by programs. Instead, the
+ * object should be manipulated using the provided functions.
+ *
+ * See @ref pcoops.
+ */
+typedef struct fsl_shw_pco_t { /* fsl_shw_platform_capabilities_object */
+} fsl_shw_pco_t;
+
+/* REQ-S2LRD-PINTFC-COA-HCO-001 */
+/*!
+ * Hash Context Object
+ *
+ * This object contains information to control hashing functions.
+
+ * No direct access to its members should be made by programs. Instead, the
+ * object should be manipulated using the provided functions. See @ref hcops.
+ */
+typedef struct fsl_shw_hco_t { /* fsl_shw_hash_context_object */
+} fsl_shw_hco_t;
+
+/*!
+ * HMAC Context Object
+ *
+ * This object contains information to control HMAC functions.
+
+ * No direct access to its members should be made by programs. Instead, the
+ * object should be manipulated using the provided functions. See @ref hmcops.
+ */
+typedef struct fsl_shw_hmco_t { /* fsl_shw_hmac_context_object */
+} fsl_shw_hmco_t;
+
+/* REQ-S2LRD-PINTFC-COA-SCCO-001 */
+/*!
+ * Symmetric Cipher Context Object
+ *
+ * This object contains information to control Symmetric Ciphering encrypt and
+ * decrypt functions in #FSL_SYM_MODE_STREAM (ARC4), #FSL_SYM_MODE_ECB,
+ * #FSL_SYM_MODE_CBC, and #FSL_SYM_MODE_CTR modes and the
+ * #fsl_shw_symmetric_encrypt() and #fsl_shw_symmetric_decrypt() functions.
+ * CCM mode is controlled with the #fsl_shw_acco_t object.
+ *
+ * No direct access to its members should be made by programs. Instead, the
+ * object should be manipulated using the provided functions. See @ref sccops.
+ */
+typedef struct fsl_shw_scco_t { /* fsl_shw_symmetric_cipher_context_object */
+} fsl_shw_scco_t;
+
+/*!
+ * Authenticate-Cipher Context Object
+
+ * An object for controlling the function of, and holding information about,
+ * data for the authenticate-cipher functions, #fsl_shw_gen_encrypt() and
+ * #fsl_shw_auth_decrypt().
+ *
+ * No direct access to its members should be made by programs. Instead, the
+ * object should be manipulated using the provided functions. See @ref
+ * accoops.
+ */
+typedef struct fsl_shw_acco_t { /* fsl_shw_authenticate_cipher_context_object */
+} fsl_shw_acco_t;
+ /*! @} *//* strgrp */
+
+/******************************************************************************
+ * Access Macros for Objects
+ *****************************************************************************/
+/*! @addtogroup pcoops
+ @{ */
+
+/*!
+ * Get FSL SHW API version
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @param[out] major A pointer to where the major version
+ * of the API is to be stored.
+ * @param[out] minor A pointer to where the minor version
+ * of the API is to be stored.
+ */
+void fsl_shw_pco_get_version(const fsl_shw_pco_t * pc_info,
+ uint32_t * major, uint32_t * minor);
+
+/*!
+ * Get underlying driver version.
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @param[out] major A pointer to where the major version
+ * of the driver is to be stored.
+ * @param[out] minor A pointer to where the minor version
+ * of the driver is to be stored.
+ */
+void fsl_shw_pco_get_driver_version(const fsl_shw_pco_t * pc_info,
+ uint32_t * major, uint32_t * minor);
+
+/*!
+ * Get list of symmetric algorithms supported.
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @param[out] algorithms A pointer to where to store the location of
+ * the list of algorithms.
+ * @param[out] algorithm_count A pointer to where to store the number of
+ * algorithms in the list at @a algorithms.
+ */
+void fsl_shw_pco_get_sym_algorithms(const fsl_shw_pco_t * pc_info,
+ fsl_shw_key_alg_t * algorithms[],
+ uint8_t * algorithm_count);
+
+/*!
+ * Get list of symmetric modes supported.
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @param[out] modes A pointer to where to store the location of
+ * the list of modes.
+ * @param[out] mode_count A pointer to where to store the number of
+ * algorithms in the list at @a modes.
+ */
+void fsl_shw_pco_get_sym_modes(const fsl_shw_pco_t * pc_info,
+ fsl_shw_sym_mode_t * modes[],
+ uint8_t * mode_count);
+
+/*!
+ * Get list of hash algorithms supported.
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @param[out] algorithms A pointer which will be set to the list of
+ * algorithms.
+ * @param[out] algorithm_count The number of algorithms in the list at @a
+ * algorithms.
+ */
+void fsl_shw_pco_get_hash_algorithms(const fsl_shw_pco_t * pc_info,
+ fsl_shw_hash_alg_t * algorithms[],
+ uint8_t * algorithm_count);
+
+/*!
+ * Determine whether the combination of a given symmetric algorithm and a given
+ * mode is supported.
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @param algorithm A Symmetric Cipher algorithm.
+ * @param mode A Symmetric Cipher mode.
+ *
+ * @return 0 if combination is not supported, non-zero if supported.
+ */
+int fsl_shw_pco_check_sym_supported(const fsl_shw_pco_t * pc_info,
+ fsl_shw_key_alg_t algorithm,
+ fsl_shw_sym_mode_t mode);
+
+/*!
+ * Determine whether a given Encryption-Authentication mode is supported.
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @param mode The Authentication mode.
+ *
+ * @return 0 if mode is not supported, non-zero if supported.
+ */
+int fsl_shw_pco_check_auth_supported(const fsl_shw_pco_t * pc_info,
+ fsl_shw_acc_mode_t mode);
+
+/*!
+ * Determine whether Black Keys (key establishment / wrapping) is supported.
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ *
+ * @return 0 if wrapping is not supported, non-zero if supported.
+ */
+int fsl_shw_pco_check_black_key_supported(const fsl_shw_pco_t * pc_info);
+
+/*!
+ * Get FSL SHW SCC driver version
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @param[out] major A pointer to where the major version
+ * of the SCC driver is to be stored.
+ * @param[out] minor A pointer to where the minor version
+ * of the SCC driver is to be stored.
+ */
+void fsl_shw_pco_get_scc_driver_version(const fsl_shw_pco_t * pc_info,
+ uint32_t * major, uint32_t * minor);
+
+/*!
+ * Get SCM hardware version
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @return The SCM hardware version
+ */
+uint32_t fsl_shw_pco_get_scm_version(const fsl_shw_pco_t * pc_info);
+
+/*!
+ * Get SMN hardware version
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @return The SMN hardware version
+ */
+uint32_t fsl_shw_pco_get_smn_version(const fsl_shw_pco_t * pc_info);
+
+/*!
+ * Get the size of an SCM block, in bytes
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @return The size of an SCM block, in bytes.
+ */
+uint32_t fsl_shw_pco_get_scm_block_size(const fsl_shw_pco_t * pc_info);
+
+/*!
+ * Get size of Black and Red RAM memory
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @param[out] black_size A pointer to where the size of the Black RAM, in
+ * blocks, is to be placed.
+ * @param[out] red_size A pointer to where the size of the Red RAM, in
+ * blocks, is to be placed.
+ */
+void fsl_shw_pco_get_smn_size(const fsl_shw_pco_t * pc_info,
+ uint32_t * black_size, uint32_t * red_size);
+
+/*!
+ * Determine whether Secure Partitions are supported
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ *
+ * @return 0 if secure partitions are not supported, non-zero if supported.
+ */
+int fsl_shw_pco_check_spo_supported(const fsl_shw_pco_t * pc_info);
+
+/*!
+ * Get the size of a Secure Partitions
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ *
+ * @return Partition size, in bytes. 0 if Secure Partitions not supported.
+ */
+uint32_t fsl_shw_pco_get_spo_size_bytes(const fsl_shw_pco_t * pc_info);
+
+/*!
+ * Get the number of Secure Partitions on this platform
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ *
+ * @return Number of partitions. 0 if Secure Partitions not supported. Note
+ * that this returns the total number of partitions, though
+ * not all may be available to the user.
+ */
+uint32_t fsl_shw_pco_get_spo_count(const fsl_shw_pco_t * pc_info);
+
+/*!
+ * Determine whether Platform Key features are available
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ *
+ * @return 1 if Programmed Key features are available, otherwise zero.
+ */
+int fsl_shw_pco_check_pk_supported(const fsl_shw_pco_t * pc_info);
+
+/*!
+ * Determine whether Software Key features are available
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ *
+ * @return 1 if Software key features are available, otherwise zero.
+ */
+int fsl_shw_pco_check_sw_keys_supported(const fsl_shw_pco_t * pc_info);
+
+/*! @} *//* pcoops */
+
+/*! @addtogroup ucoops
+ @{ */
+
+/*!
+ * Initialize a User Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the User Context Object to initial values, and set the size
+ * of the results pool. The mode will be set to a default of
+ * #FSL_UCO_BLOCKING_MODE.
+ *
+ * When using non-blocking operations, this sets the maximum number of
+ * operations which can be outstanding. This number includes the counts of
+ * operations waiting to start, operation(s) being performed, and results which
+ * have not been retrieved.
+ *
+ * Changes to this value are ignored once user registration has completed. It
+ * should be set to 1 if only blocking operations will ever be performed.
+ *
+ * @param user_ctx The User Context object to operate on.
+ * @param pool_size The maximum number of operations which can be
+ * outstanding.
+ */
+void fsl_shw_uco_init(fsl_shw_uco_t * user_ctx, uint16_t pool_size);
+
+/*!
+ * Set the User Reference for the User Context.
+ *
+ * @param user_ctx The User Context object to operate on.
+ * @param reference A value which will be passed back with a result.
+ */
+void fsl_shw_uco_set_reference(fsl_shw_uco_t * user_ctx, uint32_t reference);
+
+/*!
+ * Set the callback routine for the User Context.
+ *
+ * Note that the callback routine may be called when no results are available,
+ * and possibly even when no requests are outstanding.
+ *
+ *
+ * @param user_ctx The User Context object to operate on.
+ * @param callback_fn The function the API will invoke when an operation
+ * completes.
+ */
+void fsl_shw_uco_set_callback(fsl_shw_uco_t * user_ctx,
+ void (*callback_fn) (fsl_shw_uco_t * uco));
+
+/*!
+ * Set flags in the User Context.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param user_ctx The User Context object to operate on.
+ * @param flags ORed values from #fsl_shw_user_ctx_flags_t.
+ */
+void fsl_shw_uco_set_flags(fsl_shw_uco_t * user_ctx, uint32_t flags);
+
+/*!
+ * Clear flags in the User Context.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param user_ctx The User Context object to operate on.
+ * @param flags ORed values from #fsl_shw_user_ctx_flags_t.
+ */
+void fsl_shw_uco_clear_flags(fsl_shw_uco_t * user_ctx, uint32_t flags);
+
+/*!
+ * Select a key for the key-wrap key for key wrapping/unwrapping
+ *
+ * Without a call to this function, default is FSL_SHW_PF_KEY_IIM. The wrap
+ * key is used to encrypt and decrypt the per-key random secret which is used
+ * to calculate the key which will encrypt/decrypt the user's key.
+ *
+ * @param user_ctx The User Context object to operate on.
+ * @param pf_key Which key to use. Valid choices are
+ * #FSL_SHW_PF_KEY_IIM, #FSL_SHW_PF_KEY_RND, and
+ * #FSL_SHW_PF_KEY_IIM_RND.
+ */
+void fsl_shw_uco_set_wrap_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_pf_key_t pf_key);
+
+ /*! @} *//* ucoops */
+
+/*! @addtogroup rops
+ @{ */
+
+/*!
+ * Retrieve the status code from a Result Object.
+ *
+ * @param result The result object to query.
+ *
+ * @return The status of the request.
+ */
+fsl_shw_return_t fsl_shw_ro_get_status(fsl_shw_result_t * result);
+
+/*!
+ * Retrieve the reference value from a Result Object.
+ *
+ * @param result The result object to query.
+ *
+ * @return The reference associated with the request.
+ */
+uint32_t fsl_shw_ro_get_reference(fsl_shw_result_t * result);
+
+ /* @} *//* rops */
+
+/*! @addtogroup skoops
+ @{ */
+
+/*!
+ * Initialize a Secret Key Object.
+ *
+ * This function or #fsl_shw_sko_init_pf_key() must be called before performing
+ * any other operation with the Object.
+ *
+ * @param key_info The Secret Key Object to be initialized.
+ * @param algorithm DES, AES, etc.
+ *
+ */
+void fsl_shw_sko_init(fsl_shw_sko_t * key_info, fsl_shw_key_alg_t algorithm);
+
+/*!
+ * Initialize a Secret Key Object to use a Platform Key register.
+ *
+ * This function or #fsl_shw_sko_init() must be called before performing any
+ * other operation with the Object. #fsl_shw_sko_set_key() does not work on
+ * a key object initialized in this way.
+ *
+ * If this function is used to initialize the key object, but no key is
+ * established with the key object, then the object will refer strictly to the
+ * key value specified by the @c pf_key selection.
+ *
+ * If the pf key is #FSL_SHW_PF_KEY_PRG or #FSL_SHW_PF_KEY_IIM_PRG, then the
+ * key object may be used with #fsl_shw_establish_key() to change the Program
+ * Key value. When the pf key is neither #FSL_SHW_PF_KEY_PRG nor
+ * #FSL_SHW_PF_KEY_IIM_PRG, it is an error to call #fsl_shw_establish_key().
+ *
+ * @param key_info The Secret Key Object to be initialized.
+ * @param algorithm DES, AES, etc.
+ * @param pf_key Which platform key is referenced.
+ */
+void fsl_shw_sko_init_pf_key(fsl_shw_sko_t * key_info,
+ fsl_shw_key_alg_t algorithm,
+ fsl_shw_pf_key_t pf_key);
+
+/*!
+ * Store a cleartext key in the key object.
+ *
+ * This has the side effect of setting the #FSL_SKO_KEY_PRESENT flag. It should
+ * not be used if there is a key established with the key object. If there is,
+ * a call to #fsl_shw_release_key() should be made first.
+ *
+ * @param key_object A variable of type #fsl_shw_sko_t.
+ * @param key A pointer to the beginning of the key.
+ * @param key_length The length, in octets, of the key. The value should be
+ * appropriate to the key size supported by the algorithm.
+ * 64 octets is the absolute maximum value allowed for this
+ * call.
+ */
+void fsl_shw_sko_set_key(fsl_shw_sko_t * key_object,
+ const uint8_t * key, uint16_t key_length);
+
+/*!
+ * Set a size for the key.
+ *
+ * This function would normally be used when the user wants the key to be
+ * generated from a random source.
+ *
+ * @param key_object A variable of type #fsl_shw_sko_t.
+ * @param key_length The length, in octets, of the key. The value should be
+ * appropriate to the key size supported by the algorithm.
+ * 64 octets is the absolute maximum value allowed for this
+ * call.
+ */
+void fsl_shw_sko_set_key_length(fsl_shw_sko_t * key_object,
+ uint16_t key_length);
+
+/*!
+ * Set the User ID associated with the key.
+ *
+ * @param key_object A variable of type #fsl_shw_sko_t.
+ * @param userid The User ID to identify authorized users of the key.
+ */
+void fsl_shw_sko_set_user_id(fsl_shw_sko_t * key_object, key_userid_t userid);
+
+/*!
+ * Set the keystore that the key will be stored in.
+ *
+ * @param key_object A variable of type #fsl_shw_sko_t.
+ * @param keystore The keystore to place the key in. This is a variable of
+ * type #fsl_shw_kso_t.
+ */
+void fsl_shw_sko_set_keystore(fsl_shw_sko_t * key_object,
+ fsl_shw_kso_t * keystore);
+
+/*!
+ * Set the establish key handle into a key object.
+ *
+ * The @a userid field will be used to validate the access to the unwrapped
+ * key. This feature is not available for all platforms, nor for all
+ * algorithms and modes.
+ *
+ * The #FSL_SKO_KEY_ESTABLISHED will be set (and the #FSL_SKO_KEY_PRESENT
+ * flag will be cleared).
+ *
+ * @param key_object A variable of type #fsl_shw_sko_t.
+ * @param userid The User ID to verify this user is an authorized user of
+ * the key.
+ * @param handle A @a handle from #fsl_shw_sko_get_established_info.
+ */
+void fsl_shw_sko_set_established_info(fsl_shw_sko_t * key_object,
+ key_userid_t userid, uint32_t handle);
+
+/*!
+ * Extract the algorithm from a key object.
+ *
+ * @param key_info The Key Object to be queried.
+ * @param[out] algorithm A pointer to the location to store the algorithm.
+ */
+void fsl_shw_sko_get_algorithm(const fsl_shw_sko_t * key_info,
+ fsl_shw_key_alg_t * algorithm);
+
+/*!
+ * Retrieve the cleartext key from a key object that is stored in a user
+ * keystore.
+ *
+ * @param skobject The Key Object to be queried.
+ * @param[out] skkey A pointer to the location to store the key. NULL
+ * if the key is not stored in a user keystore.
+ */
+void fsl_shw_sko_get_key(const fsl_shw_sko_t * skobject, void *skkey);
+
+/*!
+ * Retrieve the established-key handle from a key object.
+ *
+ * @param key_object A variable of type #fsl_shw_sko_t.
+ * @param handle The location to store the @a handle of the unwrapped
+ * key.
+ */
+void fsl_shw_sko_get_established_info(fsl_shw_sko_t * key_object,
+ uint32_t * handle);
+
+/*!
+ * Determine the size of a wrapped key based upon the cleartext key's length.
+ *
+ * This function can be used to calculate the number of octets that
+ * #fsl_shw_extract_key() will write into the location at @a covered_key.
+ *
+ * If zero is returned at @a length, this means that the key length in
+ * @a key_info is not supported.
+ *
+ * @param key_info Information about a key to be wrapped.
+ * @param length Location to store the length of a wrapped
+ * version of the key in @a key_info.
+ */
+void fsl_shw_sko_calculate_wrapped_size(const fsl_shw_sko_t * key_info,
+ uint32_t * length);
+
+/*!
+ * Set some flags in the key object.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param key_object A variable of type #fsl_shw_sko_t.
+ * @param flags (One or more) ORed members of #fsl_shw_key_flags_t which
+ * are to be set.
+ */
+void fsl_shw_sko_set_flags(fsl_shw_sko_t * key_object, uint32_t flags);
+
+/*!
+ * Clear some flags in the key object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param key_object A variable of type #fsl_shw_sko_t.
+ * @param flags (One or more) ORed members of #fsl_shw_key_flags_t which
+ * are to be reset.
+ */
+void fsl_shw_sko_clear_flags(fsl_shw_sko_t * key_object, uint32_t flags);
+
+ /*! @} *//* end skoops */
+
+/*****************************************************************************/
+
+/*! @addtogroup hcops
+ @{ */
+
+/*****************************************************************************/
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-004 - partially */
+/*!
+ * Initialize a Hash Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the current message length and hash algorithm in the hash
+ * context object.
+ *
+ * @param hash_ctx The hash context to operate upon.
+ * @param algorithm The hash algorithm to be used (#FSL_HASH_ALG_MD5,
+ * #FSL_HASH_ALG_SHA256, etc).
+ *
+ */
+void fsl_shw_hco_init(fsl_shw_hco_t * hash_ctx, fsl_shw_hash_alg_t algorithm);
+
+/*****************************************************************************/
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-001 */
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-002 */
+/*!
+ * Get the current hash value and message length from the hash context object.
+ *
+ * The algorithm must have already been specified. See #fsl_shw_hco_init().
+ *
+ * @param hash_ctx The hash context to query.
+ * @param[out] digest Pointer to the location of @a length octets where to
+ * store a copy of the current value of the digest.
+ * @param length Number of octets of hash value to copy.
+ * @param[out] msg_length Pointer to the location to store the number of octets
+ * already hashed.
+ */
+void fsl_shw_hco_get_digest(const fsl_shw_hco_t * hash_ctx, uint8_t * digest,
+ uint8_t length, uint32_t * msg_length);
+
+/*****************************************************************************/
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-002 - partially */
+/*!
+ * Get the hash algorithm from the hash context object.
+ *
+ * @param hash_ctx The hash context to query.
+ * @param[out] algorithm Pointer to where the algorithm is to be stored.
+ */
+void fsl_shw_hco_get_info(const fsl_shw_hco_t * hash_ctx,
+ fsl_shw_hash_alg_t * algorithm);
+
+/*****************************************************************************/
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-003 */
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-004 */
+/*!
+ * Set the current hash value and message length in the hash context object.
+ *
+ * The algorithm must have already been specified. See #fsl_shw_hco_init().
+ *
+ * @param hash_ctx The hash context to operate upon.
+ * @param context Pointer to buffer of appropriate length to copy into
+ * the hash context object.
+ * @param msg_length The number of octets of the message which have
+ * already been hashed.
+ *
+ */
+void fsl_shw_hco_set_digest(fsl_shw_hco_t * hash_ctx, const uint8_t * context,
+ uint32_t msg_length);
+
+/*!
+ * Set flags in a Hash Context Object.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hash_ctx The hash context to be operated on.
+ * @param flags The flags to be set in the context. These can be ORed
+ * members of #fsl_shw_hash_ctx_flags_t.
+ */
+void fsl_shw_hco_set_flags(fsl_shw_hco_t * hash_ctx, uint32_t flags);
+
+/*!
+ * Clear flags in a Hash Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hash_ctx The hash context to be operated on.
+ * @param flags The flags to be reset in the context. These can be ORed
+ * members of #fsl_shw_hash_ctx_flags_t.
+ */
+void fsl_shw_hco_clear_flags(fsl_shw_hco_t * hash_ctx, uint32_t flags);
+
+ /*! @} *//* end hcops */
+
+/*****************************************************************************/
+
+/*! @addtogroup hmcops
+ @{ */
+
+/*!
+ * Initialize an HMAC Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the current message length and hash algorithm in the HMAC
+ * context object.
+ *
+ * @param hmac_ctx The HMAC context to operate upon.
+ * @param algorithm The hash algorithm to be used (#FSL_HASH_ALG_MD5,
+ * #FSL_HASH_ALG_SHA256, etc).
+ *
+ */
+void fsl_shw_hmco_init(fsl_shw_hmco_t * hmac_ctx, fsl_shw_hash_alg_t algorithm);
+
+/*!
+ * Set flags in an HMAC Context Object.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hmac_ctx The HMAC context to be operated on.
+ * @param flags The flags to be set in the context. These can be ORed
+ * members of #fsl_shw_hmac_ctx_flags_t.
+ */
+void fsl_shw_hmco_set_flags(fsl_shw_hmco_t * hmac_ctx, uint32_t flags);
+
+/*!
+ * Clear flags in an HMAC Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hmac_ctx The HMAC context to be operated on.
+ * @param flags The flags to be reset in the context. These can be ORed
+ * members of #fsl_shw_hmac_ctx_flags_t.
+ */
+void fsl_shw_hmco_clear_flags(fsl_shw_hmco_t * hmac_ctx, uint32_t flags);
+
+/*! @} */
+
+/*****************************************************************************/
+
+/*! @addtogroup sccops
+ @{ */
+
+/*!
+ * Initialize a Symmetric Cipher Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. This will set the @a mode and @a algorithm and initialize the
+ * Object.
+ *
+ * @param sym_ctx The context object to operate on.
+ * @param algorithm The cipher algorithm this context will be used with.
+ * @param mode #FSL_SYM_MODE_CBC, #FSL_SYM_MODE_ECB, etc.
+ *
+ */
+void fsl_shw_scco_init(fsl_shw_scco_t * sym_ctx,
+ fsl_shw_key_alg_t algorithm, fsl_shw_sym_mode_t mode);
+
+/*!
+ * Set the flags for a Symmetric Cipher Context.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param sym_ctx The context object to operate on.
+ * @param flags The flags to reset (one or more values from
+ * #fsl_shw_sym_ctx_flags_t ORed together).
+ *
+ */
+void fsl_shw_scco_set_flags(fsl_shw_scco_t * sym_ctx, uint32_t flags);
+
+/*!
+ * Clear some flags in a Symmetric Cipher Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param sym_ctx The context object to operate on.
+ * @param flags The flags to reset (one or more values from
+ * #fsl_shw_sym_ctx_flags_t ORed together).
+ *
+ */
+void fsl_shw_scco_clear_flags(fsl_shw_scco_t * sym_ctx, uint32_t flags);
+
+/*!
+ * Set the Context (IV) for a Symmetric Cipher Context.
+ *
+ * This is to set the context/IV for #FSL_SYM_MODE_CBC mode, or to set the
+ * context (the S-Box and pointers) for ARC4. The full context size will
+ * be copied.
+ *
+ * @param sym_ctx The context object to operate on.
+ * @param context A pointer to the buffer which contains the context.
+ *
+ */
+void fsl_shw_scco_set_context(fsl_shw_scco_t * sym_ctx, uint8_t * context);
+
+/*!
+ * Get the Context for a Symmetric Cipher Context.
+ *
+ * This is to retrieve the context/IV for #FSL_SYM_MODE_CBC mode, or to
+ * retrieve context (the S-Box and pointers) for ARC4. The full context
+ * will be copied.
+ *
+ * @param sym_ctx The context object to operate on.
+ * @param[out] context Pointer to location where context will be stored.
+ */
+void fsl_shw_scco_get_context(const fsl_shw_scco_t * sym_ctx,
+ uint8_t * context);
+
+/*!
+ * Set the Counter Value for a Symmetric Cipher Context.
+ *
+ * This will set the Counter Value for CTR mode.
+ *
+ * @param sym_ctx The context object to operate on.
+ * @param counter The starting counter value. The number of octets.
+ * copied will be the block size for the algorithm.
+ * @param modulus The modulus for controlling the incrementing of the counter.
+ *
+ */
+void fsl_shw_scco_set_counter_info(fsl_shw_scco_t * sym_ctx,
+ const uint8_t * counter,
+ fsl_shw_ctr_mod_t modulus);
+
+/*!
+ * Get the Counter Value for a Symmetric Cipher Context.
+ *
+ * This will retrieve the Counter Value is for CTR mode.
+ *
+ * @param sym_ctx The context object to query.
+ * @param[out] counter Pointer to location to store the current counter
+ * value. The number of octets copied will be the
+ * block size for the algorithm.
+ * @param[out] modulus Pointer to location to store the modulus.
+ *
+ */
+void fsl_shw_scco_get_counter_info(const fsl_shw_scco_t * sym_ctx,
+ uint8_t * counter,
+ fsl_shw_ctr_mod_t * modulus);
+
+ /*! @} *//* end sccops */
+
+/*****************************************************************************/
+
+/*! @addtogroup accoops
+ @{ */
+
+/*!
+ * Initialize a Authentication-Cipher Context.
+ *
+ * @param auth_object Pointer to object to operate on.
+ * @param mode The mode for this object (only #FSL_ACC_MODE_CCM
+ * supported).
+ */
+void fsl_shw_acco_init(fsl_shw_acco_t * auth_object, fsl_shw_acc_mode_t mode);
+
+/*!
+ * Set the flags for a Authentication-Cipher Context.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param auth_object Pointer to object to operate on.
+ * @param flags The flags to set (one or more from
+ * #fsl_shw_auth_ctx_flags_t ORed together).
+ *
+ */
+void fsl_shw_acco_set_flags(fsl_shw_acco_t * auth_object, uint32_t flags);
+
+/*!
+ * Clear some flags in a Authentication-Cipher Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param auth_object Pointer to object to operate on.
+ * @param flags The flags to reset (one or more from
+ * #fsl_shw_auth_ctx_flags_t ORed together).
+ *
+ */
+void fsl_shw_acco_clear_flags(fsl_shw_acco_t * auth_object, uint32_t flags);
+
+/*!
+ * Set up the Authentication-Cipher Object for CCM mode.
+ *
+ * This will set the @a auth_object for CCM mode and save the @a ctr,
+ * and @a mac_length. This function can be called instead of
+ * #fsl_shw_acco_init().
+ *
+ * The parameter @a ctr is Counter Block 0, (counter value 0), which is for the
+ * MAC.
+ *
+ * @param auth_object Pointer to object to operate on.
+ * @param algorithm Cipher algorithm. Only AES is supported.
+ * @param ctr The initial counter value.
+ * @param mac_length The number of octets used for the MAC. Valid values are
+ * 4, 6, 8, 10, 12, 14, and 16.
+ */
+void fsl_shw_acco_set_ccm(fsl_shw_acco_t * auth_object,
+ fsl_shw_key_alg_t algorithm,
+ const uint8_t * ctr, uint8_t mac_length);
+
+/*!
+ * Format the First Block (IV) & Initial Counter Value per NIST CCM.
+ *
+ * This function will also set the IV and CTR values per Appendix A of NIST
+ * Special Publication 800-38C (May 2004). It will also perform the
+ * #fsl_shw_acco_set_ccm() operation with information derived from this set of
+ * parameters.
+ *
+ * Note this function assumes the algorithm is AES. It initializes the
+ * @a auth_object by setting the mode to #FSL_ACC_MODE_CCM and setting the
+ * flags to be #FSL_ACCO_NIST_CCM.
+ *
+ * @param auth_object Pointer to object to operate on.
+ * @param t_length The number of octets used for the MAC. Valid values are
+ * 4, 6, 8, 10, 12, 14, and 16.
+ * @param ad_length Number of octets of Associated Data (may be zero).
+ * @param q_length A value for the size of the length of @a q field. Valid
+ * values are 1-8.
+ * @param n The Nonce (packet number or other changing value). Must
+ * be (15 - @a q_length) octets long.
+ * @param q The value of Q (size of the payload in octets).
+ *
+ */
+void fsl_shw_ccm_nist_format_ctr_and_iv(fsl_shw_acco_t * auth_object,
+ uint8_t t_length,
+ uint32_t ad_length,
+ uint8_t q_length,
+ const uint8_t * n, uint32_t q);
+
+/*!
+ * Update the First Block (IV) & Initial Counter Value per NIST CCM.
+ *
+ * This function will set the IV and CTR values per Appendix A of NIST Special
+ * Publication 800-38C (May 2004).
+ *
+ * Note this function assumes that #fsl_shw_ccm_nist_format_ctr_and_iv() has
+ * previously been called on the @a auth_object.
+ *
+ * @param auth_object Pointer to object to operate on.
+ * @param n The Nonce (packet number or other changing value). Must
+ * be (15 - @a q_length) octets long.
+ * @param q The value of Q (size of the payload in octets).
+ *
+ */
+void fsl_shw_ccm_nist_update_ctr_and_iv(fsl_shw_acco_t * auth_object,
+ const uint8_t * n, uint32_t q);
+
+ /* @} *//* accoops */
+
+/******************************************************************************
+ * Library functions
+ *****************************************************************************/
+
+/*! @addtogroup miscfuns
+ @{ */
+
+/* REQ-S2LRD-PINTFC-API-GEN-003 */
+/*!
+ * Determine the hardware security capabilities of this platform.
+ *
+ * Though a user context object is passed into this function, it will always
+ * act in a non-blocking manner.
+ *
+ * @param user_ctx The user context which will be used for the query.
+ *
+ * @return A pointer to the capabilities object.
+ */
+extern fsl_shw_pco_t *fsl_shw_get_capabilities(fsl_shw_uco_t * user_ctx);
+
+/* REQ-S2LRD-PINTFC-API-GEN-004 */
+/*!
+ * Create an association between the user and the provider of the API.
+ *
+ * @param user_ctx The user context which will be used for this association.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_register_user(fsl_shw_uco_t * user_ctx);
+
+/* REQ-S2LRD-PINTFC-API-GEN-005 */
+/*!
+ * Destroy the association between the user and the provider of the API.
+ *
+ * @param user_ctx The user context which is no longer needed.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_deregister_user(fsl_shw_uco_t * user_ctx);
+
+/* REQ-S2LRD-PINTFC-API-GEN-006 */
+/*!
+ * Retrieve results from earlier operations.
+ *
+ * @param user_ctx The user's context.
+ * @param result_size The number of array elements of @a results.
+ * @param[in,out] results Pointer to first of the (array of) locations to
+ * store results.
+ * @param[out] result_count Pointer to store the number of results which
+ * were returned.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_get_results(fsl_shw_uco_t * user_ctx,
+ uint16_t result_size,
+ fsl_shw_result_t results[],
+ uint16_t * result_count);
+
+/*!
+ * Allocate a block of secure memory
+ *
+ * @param user_ctx User context
+ * @param size Memory size (octets). Note: currently only
+ * supports only single-partition sized blocks.
+ * @param UMID User Mode ID to use when registering the
+ * partition.
+ * @param permissions Permissions to initialize the partition with.
+ * Can be made by ORing flags from the
+ * #fsl_shw_permission_t.
+ *
+ * @return Address of the allocated memory. NULL if the
+ * call was not successful.
+ */
+extern void *fsl_shw_smalloc(fsl_shw_uco_t * user_ctx,
+ uint32_t size,
+ const uint8_t * UMID, uint32_t permissions);
+
+/*!
+ * Free a block of secure memory that was allocated with #fsl_shw_smalloc
+ *
+ * @param user_ctx User context
+ * @param address Address of the block of secure memory to be
+ * released.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_sfree(fsl_shw_uco_t * user_ctx, void *address);
+
+/*!
+ * Diminish the permissions of a block of secure memory. Note that permissions
+ * can only be revoked.
+ *
+ * @param user_ctx User context
+ * @param address Base address of the secure memory to work with
+ * @param permissions Permissions to initialize the partition with.
+ * Can be made by ORing flags from the
+ * #fsl_shw_permission_t.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_diminish_perms(fsl_shw_uco_t * user_ctx,
+ void *address,
+ uint32_t permissions);
+
+/*!
+ * @brief Encrypt a region of secure memory using the hardware secret key
+ *
+ * @param user_ctx User context
+ * @param partition_base Base address of the partition
+ * @param offset_bytes Offset of data from the partition base
+ * @param byte_count Length of the data to encrypt
+ * @param black_data Location to store the encrypted data
+ * @param IV IV to use for the encryption routine
+ * @param cypher_mode Cyphering mode to use, specified by type
+ * #fsl_shw_cypher_mode_t
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t
+do_scc_encrypt_region(fsl_shw_uco_t * user_ctx,
+ void *partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, uint8_t * black_data,
+ uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode);
+
+/*!
+ * @brief Decrypt a region of secure memory using the hardware secret key
+ *
+ * @param user_ctx User context
+ * @param partition_base Base address of the partition
+ * @param offset_bytes Offset of data from the partition base
+ * @param byte_count Length of the data to encrypt
+ * @param black_data Location to store the encrypted data
+ * @param IV IV to use for the encryption routine
+ * @param cypher_mode Cyphering mode to use, specified by type
+ * #fsl_shw_cypher_mode_t
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t
+do_scc_decrypt_region(fsl_shw_uco_t * user_ctx,
+ void *partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, const uint8_t * black_data,
+ uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode);
+
+ /*! @} *//* miscfuns */
+
+/*! @addtogroup opfuns
+ @{ */
+
+/* REQ-S2LRD-PINTFC-API-BASIC-SYM-002 */
+/* PINTFC-API-BASIC-SYM-ARC4-001 */
+/* PINTFC-API-BASIC-SYM-ARC4-002 */
+/*!
+ * Encrypt a stream of data with a symmetric-key algorithm.
+ *
+ * In ARC4, and also in #FSL_SYM_MODE_CBC and #FSL_SYM_MODE_CTR modes, the
+ * flags of the @a sym_ctx object will control part of the operation of this
+ * function. The #FSL_SYM_CTX_INIT flag means that there is no context info in
+ * the object. The #FSL_SYM_CTX_LOAD means to use information in the
+ * @a sym_ctx at the start of the operation, and the #FSL_SYM_CTX_SAVE flag
+ * means to update the object's context information after the operation has
+ * been performed.
+ *
+ * All of the data for an operation can be run through at once using the
+ * #FSL_SYM_CTX_INIT or #FSL_SYM_CTX_LOAD flags, as appropriate, and then using
+ * a @a length for the whole of the data.
+ *
+ * If a #FSL_SYM_CTX_SAVE flag were added, an additional call to the function
+ * would "pick up" where the previous call left off, allowing the user to
+ * perform the larger function in smaller steps.
+ *
+ * In #FSL_SYM_MODE_CBC and #FSL_SYM_MODE_ECB modes, the @a length must always
+ * be a multiple of the block size for the algorithm being used. For proper
+ * operation in #FSL_SYM_MODE_CTR mode, the @a length must be a multiple of the
+ * block size until the last operation on the total octet stream.
+ *
+ * Some users of ARC4 may want to compute the context (S-Box and pointers) from
+ * the key before any data is available. This may be done by running this
+ * function with a @a length of zero, with the init & save flags flags on in
+ * the @a sym_ctx. Subsequent operations would then run as normal with the
+ * load and save flags. Note that they key object is still required.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info Key and algorithm being used for this operation.
+ * @param[in,out] sym_ctx Info on cipher mode, state of the cipher.
+ * @param length Length, in octets, of the pt (and ct).
+ * @param pt pointer to plaintext to be encrypted.
+ * @param[out] ct pointer to where to store the resulting ciphertext.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ *
+ */
+extern fsl_shw_return_t fsl_shw_symmetric_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * pt,
+ uint8_t * ct);
+
+/* PINTFC-API-BASIC-SYM-002 */
+/* PINTFC-API-BASIC-SYM-ARC4-001 */
+/* PINTFC-API-BASIC-SYM-ARC4-002 */
+/*!
+ * Decrypt a stream of data with a symmetric-key algorithm.
+ *
+ * In ARC4, and also in #FSL_SYM_MODE_CBC and #FSL_SYM_MODE_CTR modes, the
+ * flags of the @a sym_ctx object will control part of the operation of this
+ * function. The #FSL_SYM_CTX_INIT flag means that there is no context info in
+ * the object. The #FSL_SYM_CTX_LOAD means to use information in the
+ * @a sym_ctx at the start of the operation, and the #FSL_SYM_CTX_SAVE flag
+ * means to update the object's context information after the operation has
+ * been performed.
+ *
+ * All of the data for an operation can be run through at once using the
+ * #FSL_SYM_CTX_INIT or #FSL_SYM_CTX_LOAD flags, as appropriate, and then using
+ * a @a length for the whole of the data.
+ *
+ * If a #FSL_SYM_CTX_SAVE flag were added, an additional call to the function
+ * would "pick up" where the previous call left off, allowing the user to
+ * perform the larger function in smaller steps.
+ *
+ * In #FSL_SYM_MODE_CBC and #FSL_SYM_MODE_ECB modes, the @a length must always
+ * be a multiple of the block size for the algorithm being used. For proper
+ * operation in #FSL_SYM_MODE_CTR mode, the @a length must be a multiple of the
+ * block size until the last operation on the total octet stream.
+ *
+ * Some users of ARC4 may want to compute the context (S-Box and pointers) from
+ * the key before any data is available. This may be done by running this
+ * function with a @a length of zero, with the #FSL_SYM_CTX_INIT &
+ * #FSL_SYM_CTX_SAVE flags on in the @a sym_ctx. Subsequent operations would
+ * then run as normal with the load & save flags. Note that they key object is
+ * still required.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The key and algorithm being used in this operation.
+ * @param[in,out] sym_ctx Info on cipher mode, state of the cipher.
+ * @param length Length, in octets, of the ct (and pt).
+ * @param ct pointer to ciphertext to be decrypted.
+ * @param[out] pt pointer to where to store the resulting plaintext.
+ *
+ * @return A return code of type #fsl_shw_return_t
+ *
+ */
+extern fsl_shw_return_t fsl_shw_symmetric_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * ct,
+ uint8_t * pt);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-005 */
+/*!
+ * Hash a stream of data with a cryptographic hash algorithm.
+ *
+ * The flags in the @a hash_ctx control the operation of this function.
+ *
+ * Hashing functions work on 64 octets of message at a time. Therefore, when
+ * any partial hashing of a long message is performed, the message @a length of
+ * each segment must be a multiple of 64. When ready to
+ * #FSL_HASH_FLAGS_FINALIZE the hash, the @a length may be any value.
+ *
+ * With the #FSL_HASH_FLAGS_INIT and #FSL_HASH_FLAGS_FINALIZE flags on, a
+ * one-shot complete hash, including padding, will be performed. The @a length
+ * may be any value.
+ *
+ * The first octets of a data stream can be hashed by setting the
+ * #FSL_HASH_FLAGS_INIT and #FSL_HASH_FLAGS_SAVE flags. The @a length must be
+ * a multiple of 64.
+ *
+ * The flag #FSL_HASH_FLAGS_LOAD is used to load a context previously saved by
+ * #FSL_HASH_FLAGS_SAVE. The two in combination will allow a (multiple-of-64
+ * octets) 'middle sequence' of the data stream to be hashed with the
+ * beginning. The @a length must again be a multiple of 64.
+ *
+ * Since the flag #FSL_HASH_FLAGS_LOAD is used to load a context previously
+ * saved by #FSL_HASH_FLAGS_SAVE, the #FSL_HASH_FLAGS_LOAD and
+ * #FSL_HASH_FLAGS_FINALIZE flags, used together, can be used to finish the
+ * stream. The @a length may be any value.
+ *
+ * If the user program wants to do the padding for the hash, it can leave off
+ * the #FSL_HASH_FLAGS_FINALIZE flag. The @a length must then be a multiple of
+ * 64 octets.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] hash_ctx Hashing algorithm and state of the cipher.
+ * @param msg Pointer to the data to be hashed.
+ * @param length Length, in octets, of the @a msg.
+ * @param[out] result If not null, pointer to where to store the hash
+ * digest.
+ * @param result_len Number of octets to store in @a result.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_hash(fsl_shw_uco_t * user_ctx,
+ fsl_shw_hco_t * hash_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HMAC-001 */
+/*!
+ * Precompute the Key hashes for an HMAC operation.
+ *
+ * This function may be used to calculate the inner and outer precomputes,
+ * which are the hash contexts resulting from hashing the XORed key for the
+ * 'inner hash' and the 'outer hash', respectively, of the HMAC function.
+ *
+ * After execution of this function, the @a hmac_ctx will contain the
+ * precomputed inner and outer contexts, so that they may be used by
+ * #fsl_shw_hmac(). The flags of @a hmac_ctx will be updated with
+ * #FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT to mark their presence. In addition, the
+ * #FSL_HMAC_FLAGS_INIT flag will be set.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The key being used in this operation. Key must be
+ * 1 to 64 octets long.
+ * @param[in,out] hmac_ctx The context which controls, by its flags and
+ * algorithm, the operation of this function.
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_hmac_precompute(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HMAC-002 */
+/*!
+ * Continue, finalize, or one-shot an HMAC operation.
+ *
+ * There are a number of ways to use this function. The flags in the
+ * @a hmac_ctx object will determine what operations occur.
+ *
+ * If #FSL_HMAC_FLAGS_INIT is set, then the hash will be started either from
+ * the @a key_info, or from the precomputed inner hash value in the
+ * @a hmac_ctx, depending on the value of #FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT.
+ *
+ * If, instead, #FSL_HMAC_FLAGS_LOAD is set, then the hash will be continued
+ * from the ongoing inner hash computation in the @a hmac_ctx.
+ *
+ * If #FSL_HMAC_FLAGS_FINALIZE are set, then the @a msg will be padded, hashed,
+ * the outer hash will be performed, and the @a result will be generated.
+ *
+ * If the #FSL_HMAC_FLAGS_SAVE flag is set, then the (ongoing or final) digest
+ * value will be stored in the ongoing inner hash computation field of the @a
+ * hmac_ctx.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info If #FSL_HMAC_FLAGS_INIT is set in the @a hmac_ctx,
+ * this is the key being used in this operation, and the
+ * IPAD. If #FSL_HMAC_FLAGS_INIT is set in the @a
+ * hmac_ctx and @a key_info is NULL, then
+ * #fsl_shw_hmac_precompute() has been used to populate
+ * the @a inner_precompute and @a outer_precompute
+ * contexts. If #FSL_HMAC_FLAGS_INIT is not set, this
+ * parameter is ignored.
+
+ * @param[in,out] hmac_ctx The context which controls, by its flags and
+ * algorithm, the operation of this function.
+ * @param msg Pointer to the message to be hashed.
+ * @param length Length, in octets, of the @a msg.
+ * @param[out] result Pointer, of @a result_len octets, to where to
+ * store the HMAC.
+ * @param result_len Length of @a result buffer.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_hmac(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-RNG-002 */
+/*!
+ * Get random data.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param length The number of octets of @a data being requested.
+ * @param[out] data A pointer to a location of @a length octets to where
+ * random data will be returned.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-RNG-002 */
+/*!
+ * Add entropy to random number generator.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param length Number of bytes at @a data.
+ * @param data Entropy to add to random number generator.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_add_entropy(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data);
+
+/*!
+ * Perform Generation-Encryption by doing a Cipher and a Hash.
+ *
+ * Generate the authentication value @a auth_value as well as encrypt the @a
+ * payload into @a ct (the ciphertext). This is a one-shot function, so all of
+ * the @a auth_data and the total message @a payload must passed in one call.
+ * This also means that the flags in the @a auth_ctx must be #FSL_ACCO_CTX_INIT
+ * and #FSL_ACCO_CTX_FINALIZE.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param auth_ctx Controlling object for Authenticate-decrypt.
+ * @param cipher_key_info The key being used for the cipher part of this
+ * operation. In CCM mode, this key is used for
+ * both parts.
+ * @param auth_key_info The key being used for the authentication part
+ * of this operation. In CCM mode, this key is
+ * ignored and may be NULL.
+ * @param auth_data_length Length, in octets, of @a auth_data.
+ * @param auth_data Data to be authenticated but not encrypted.
+ * @param payload_length Length, in octets, of @a payload.
+ * @param payload Pointer to the plaintext to be encrypted.
+ * @param[out] ct Pointer to the where the encrypted @a payload
+ * will be stored. Must be @a payload_length
+ * octets long.
+ * @param[out] auth_value Pointer to where the generated authentication
+ * field will be stored. Must be as many octets as
+ * indicated by MAC length in the @a function_ctx.
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_gen_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * cipher_key_info,
+ fsl_shw_sko_t * auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t * auth_data,
+ uint32_t payload_length,
+ const uint8_t * payload,
+ uint8_t * ct, uint8_t * auth_value);
+
+/*!
+ * Perform Authentication-Decryption in Cipher + Hash.
+ *
+ * This function will perform a one-shot decryption of a data stream as well as
+ * authenticate the authentication value. This is a one-shot function, so all
+ * of the @a auth_data and the total message @a payload must passed in one
+ * call. This also means that the flags in the @a auth_ctx must be
+ * #FSL_ACCO_CTX_INIT and #FSL_ACCO_CTX_FINALIZE.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param auth_ctx Controlling object for Authenticate-decrypt.
+ * @param cipher_key_info The key being used for the cipher part of this
+ * operation. In CCM mode, this key is used for
+ * both parts.
+ * @param auth_key_info The key being used for the authentication part
+ * of this operation. In CCM mode, this key is
+ * ignored and may be NULL.
+ * @param auth_data_length Length, in octets, of @a auth_data.
+ * @param auth_data Data to be authenticated but not decrypted.
+ * @param payload_length Length, in octets, of @a ct and @a pt.
+ * @param ct Pointer to the encrypted input stream.
+ * @param auth_value The (encrypted) authentication value which will
+ * be authenticated. This is the same data as the
+ * (output) @a auth_value argument to
+ * #fsl_shw_gen_encrypt().
+ * @param[out] payload Pointer to where the plaintext resulting from
+ * the decryption will be stored.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_auth_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * cipher_key_info,
+ fsl_shw_sko_t * auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t * auth_data,
+ uint32_t payload_length,
+ const uint8_t * ct,
+ const uint8_t * auth_value,
+ uint8_t * payload);
+
+/*!
+ * Establish the key in a protected location, which can be the system keystore,
+ * user keystore, or (on platforms that support it) as a Platform Key.
+ *
+ * By default, keys initialized with #fsl_shw_sko_init() will be placed into
+ * the system keystore. The user can cause the key to be established in a
+ * user keystore by first calling #fsl_shw_sko_set_keystore() on the key.
+ * Normally, keys in the system keystore can only be used for hardware
+ * encrypt or decrypt operations, however if the #FSL_SKO_KEY_SW_KEY flag is
+ * applied using #fsl_shw_sko_set_flags(), the key will be established as a
+ * software key, which can then be read out using #fsl_shw_read_key().
+ *
+ * Keys initialized with #fsl_shw_sko_init_pf_key() are established as a
+ * Platform Key. Their use is covered in @ref di_sec.
+ *
+ * This function only needs to be used when unwrapping a key, setting up a key
+ * which could be wrapped with a later call to #fsl_shw_extract_key(), or
+ * setting up a key as a Platform Key. Normal cleartext keys can simply be
+ * placed into #fsl_shw_sko_t key objects with #fsl_shw_sko_set_key() and used
+ * directly.
+ *
+ * The maximum key size supported for wrapped/unwrapped keys is 32 octets.
+ * (This is the maximum reasonable key length on Sahara - 32 octets for an HMAC
+ * key based on SHA-256.) The key size is determined by the @a key_info. The
+ * expected length of @a key can be determined by
+ * #fsl_shw_sko_calculate_wrapped_size()
+ *
+ * The protected key will not be available for use until this operation
+ * successfully completes.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] key_info The information about the key to be which will
+ * be established. In the create case, the key
+ * length must be set.
+ * @param establish_type How @a key will be interpreted to establish a
+ * key for use.
+ * @param key If @a establish_type is #FSL_KEY_WRAP_UNWRAP,
+ * this is the location of a wrapped key. If
+ * @a establish_type is #FSL_KEY_WRAP_CREATE, this
+ * parameter can be @a NULL. If @a establish_type
+ * is #FSL_KEY_WRAP_ACCEPT, this is the location
+ * of a plaintext key.
+ */
+extern fsl_shw_return_t fsl_shw_establish_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_key_wrap_t establish_type,
+ const uint8_t * key);
+
+/*!
+ * Read the key value from a key object.
+ *
+ * Only a key marked as a software key (#FSL_SKO_KEY_SW_KEY) can be read with
+ * this call. It has no effect on the status of the key store.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The referenced key.
+ * @param[out] key The location to store the key value.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_read_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ uint8_t * key);
+
+/*!
+ * Wrap a key and retrieve the wrapped value.
+ *
+ * A wrapped key is a key that has been cryptographically obscured. It is
+ * only able to be used with keys that have been established by
+ * #fsl_shw_establish_key().
+ *
+ * For keys established in the system or user keystore, this function will
+ * also release the key (see #fsl_shw_release_key()) so that it must be re-
+ * established before reuse. This function will not release keys that are
+ * established as a Platform Key, so a call to #fsl_shw_release_key() is
+ * necessary to release those keys.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The information about the key to be deleted.
+ * @param[out] covered_key The location to store the wrapped key.
+ * (This size is based upon the maximum key size
+ * of 32 octets).
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_extract_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ uint8_t * covered_key);
+
+/*!
+ * De-establish a key so that it can no longer be accessed.
+ *
+ * The key will need to be re-established before it can again be used.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The information about the key to be deleted.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_release_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info);
+
+/*!
+ * Cause the hardware to create a new random key for use by the secure memory
+ * encryption hardware.
+ *
+ * Have the hardware use the secure hardware random number generator to load a
+ * new secret key into the system's Random Key register.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_gen_random_pf_key(fsl_shw_uco_t * user_ctx);
+
+/*!
+ * Retrieve the detected tamper event.
+ *
+ * Note that if more than one event was detected, this routine will only ever
+ * return one of them.
+ *
+ * @param[in] user_ctx A user context from #fsl_shw_register_user().
+ * @param[out] tamperp Location to store the tamper information.
+ * @param[out] timestampp Locate to store timestamp from hardwhare when
+ * an event was detected.
+ *
+ *
+ * @return A return code of type #fsl_shw_return_t (for instance, if the platform
+ * is not in a fail state.
+ */
+extern fsl_shw_return_t fsl_shw_read_tamper_event(fsl_shw_uco_t * user_ctx,
+ fsl_shw_tamper_t * tamperp,
+ uint64_t * timestampp);
+
+/*! @} *//* opfuns */
+
+/* Insert example code into the API documentation. */
+
+/*!
+ * @example apitest.c
+ */
+
+/*!
+ * @example sym.c
+ */
+
+/*!
+ * @example rand.c
+ */
+
+/*!
+ * @example hash.c
+ */
+
+/*!
+ * @example hmac1.c
+ */
+
+/*!
+ * @example hmac2.c
+ */
+
+/*!
+ * @example gen_encrypt.c
+ */
+
+/*!
+ * @example auth_decrypt.c
+ */
+
+/*!
+ * @example wrapped_key.c
+ */
+
+/*!
+ * @example smalloc.c
+ */
+
+/*!
+ * @example user_keystore.c
+ */
+
+/*!
+ * @example dryice.c
+ */
+
+#endif /* API_DOC */
+
+#endif /* FSL_SHW_H */
diff --git a/drivers/mxc/security/sahara2/include/fsl_shw_keystore.h b/drivers/mxc/security/sahara2/include/fsl_shw_keystore.h
new file mode 100644
index 000000000000..4af6b782af6b
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/fsl_shw_keystore.h
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+
+#ifndef FSL_SHW_KEYSTORE_H
+#define FSL_SHW_KEYSTORE_H
+
+/*!
+ * @file fsl_shw_keystore.h
+ *
+ * @brief Definition of the User Keystore API.
+ *
+ */
+
+/*! \page user_keystore User Keystore API
+ *
+ * Definition of the User Keystore API.
+ *
+ * On platforms with multiple partitions of Secure Memory, the Keystore Object
+ * (#fsl_shw_kso_t) is provided to allow users to manage a private keystore for
+ * use in software cryptographic routines. The user can define a custom set of
+ * methods for managing their keystore, or use a default keystore handler. The
+ * keystore is established by #fsl_shw_establish_keystore(), and released by
+ * #fsl_shw_release_keystore(). The intent of this design is to make the
+ * keystore implementation as flexible as possible.
+ *
+ * See @ref keystore_api for the generic keystore API, and @ref
+ * default_keystore for the default keystore implementation.
+ *
+ */
+
+/*!
+ * @defgroup keystore_api User Keystore API
+ *
+ * Keystore API
+ *
+ * These functions define the generic keystore API, which can be used in
+ * conjunction with a keystore implementation backend to support a user
+ * keystore.
+ */
+
+/*!
+ * @defgroup default_keystore Default Keystore Implementation
+ *
+ * Default Keystore Implementation
+ *
+ * These functions define the default keystore implementation, which is used
+ * for the system keystore and for user keystores initialized by
+ * #fsl_shw_init_keystore_default(). They can be used as-is or as a reference
+ * for creating a custom keystore handler. It uses an entire Secure Memory
+ * partition, divided in to equal slots of length #KEYSTORE_SLOT_SIZE. These
+ * functions are not intended to be used directly- all user interaction with
+ * the keystore should be through the @ref keystore_api and the Wrapped Key
+ * interface.
+ *
+ * The current implementation is designed to work with both SCC and SCC2.
+ * Differences between the two versions are noted below.
+ */
+
+/*! @addtogroup keystore_api
+ @{ */
+
+#ifndef KEYSTORE_SLOT_SIZE
+/*! Size of each key slot, in octets. This sets an upper bound on the size
+ * of a key that can placed in the keystore.
+ */
+#define KEYSTORE_SLOT_SIZE 32
+#endif
+
+/*!
+ * Initialize a Keystore Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It allows the user to associate a custom keystore interface by
+ * specifying the correct set of functions that will be used to perform actions
+ * on the keystore object. To use the default keystore handler, the function
+ * #fsl_shw_init_keystore_default() can be used instead.
+ *
+ * @param keystore The Keystore object to operate on.
+ * @param data_init Keystore initialization function. This function is
+ * responsible for initializing the keystore. A
+ * user-defined object can be assigned to the user_data
+ * pointer, and will be passed to any function acting on
+ * that keystore. It is called during
+ * #fsl_shw_establish_keystore().
+ * @param data_cleanup Keystore cleanup function. This function cleans up
+ * any data structures associated with the keyboard. It
+ * is called by #fsl_shw_release_keystore().
+ * @param slot_alloc Slot allocation function. This function allocates a
+ * key slot, potentially based on size and owner id. It
+ * is called by #fsl_shw_establish_key().
+ * @param slot_dealloc Slot deallocation function.
+ * @param slot_verify_access Function to verify that a given Owner ID
+ * credential matches the given slot.
+ * @param slot_get_address For SCC2: Get the virtual address (kernel or
+ * userspace) of the data stored in the slot.
+ * For SCC: Get the physical address of the data
+ * stored in the slot.
+ * @param slot_get_base For SCC2: Get the (virtual) base address of the
+ * partition that the slot is located on.
+ * For SCC: Not implemented.
+ * @param slot_get_offset For SCC2: Get the offset from the start of the
+ * partition that the slot data is located at (in
+ * octets)
+ * For SCC: Not implemented.
+ * @param slot_get_slot_size Get the size of the key slot, in octets.
+ */
+extern void fsl_shw_init_keystore(fsl_shw_kso_t * keystore,
+ fsl_shw_return_t(*data_init) (fsl_shw_uco_t *
+ user_ctx,
+ void
+ **user_data),
+ void (*data_cleanup) (fsl_shw_uco_t *
+ user_ctx,
+ void **user_data),
+ fsl_shw_return_t(*slot_alloc) (void
+ *user_data,
+ uint32_t size,
+ uint64_t
+ owner_id,
+ uint32_t *
+ slot),
+ fsl_shw_return_t(*slot_dealloc) (void
+ *user_data,
+ uint64_t
+ owner_id,
+ uint32_t
+ slot),
+ fsl_shw_return_t(*slot_verify_access) (void
+ *user_data,
+ uint64_t
+ owner_id,
+ uint32_t
+ slot),
+ void *(*slot_get_address) (void *user_data,
+ uint32_t handle),
+ uint32_t(*slot_get_base) (void *user_data,
+ uint32_t handle),
+ uint32_t(*slot_get_offset) (void *user_data,
+ uint32_t handle),
+ uint32_t(*slot_get_slot_size) (void
+ *user_data,
+ uint32_t
+ handle));
+
+/*!
+ * Initialize a Keystore Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the user keystore object up to use the default keystore
+ * handler. If a custom keystore handler is desired, the function
+ * #fsl_shw_init_keystore() can be used instead.
+ *
+ * @param keystore The Keystore object to operate on.
+ */
+extern void fsl_shw_init_keystore_default(fsl_shw_kso_t * keystore);
+
+/*!
+ * Establish a Keystore Object.
+ *
+ * This function establishes a keystore object that has been set up by a call
+ * to #fsl_shw_init_keystore(). It is a wrapper for the user-defined
+ * data_init() function, which is specified during keystore initialization.
+ *
+ * @param user_ctx The user context that this keystore should be attached
+ * to
+ * @param keystore The Keystore object to operate on.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_establish_keystore(fsl_shw_uco_t * user_ctx,
+ fsl_shw_kso_t * keystore);
+
+/*!
+ * Release a Keystore Object.
+ *
+ * This function releases an established keystore object. It is a wrapper for
+ * the user-defined data_cleanup() function, which is specified during keystore
+ * initialization.
+ *
+ * @param user_ctx The user context that this keystore should be attached
+ * to.
+ * @param keystore The Keystore object to operate on.
+ */
+extern void fsl_shw_release_keystore(fsl_shw_uco_t * user_ctx,
+ fsl_shw_kso_t * keystore);
+
+/*!
+ * Allocate a slot in the Keystore.
+ *
+ * This function attempts to allocate a slot to hold a key in the keystore. It
+ * is called by #fsl_shw_establish_key() when establishing a Secure Key Object,
+ * if the key has been flagged to be stored in a user keystore by the
+ * #fsl_shw_sko_set_keystore() function. It is a wrapper for the
+ * implementation-specific function slot_alloc().
+ *
+ * @param keystore The Keystore object to operate on.
+ * @param[in] size Size of the key to be stored (octets).
+ * @param[in] owner_id ID of the key owner.
+ * @param[out] slot If successful, assigned slot ID
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t keystore_slot_alloc(fsl_shw_kso_t * keystore,
+ uint32_t size,
+ uint64_t owner_id, uint32_t * slot);
+
+/*!
+ * Deallocate a slot in the Keystore.
+ *
+ * This function attempts to allocate a slot to hold a key in the keystore.
+ * It is called by #fsl_shw_extract_key() and #fsl_shw_release_key() when the
+ * key that it contains is to be released. It is a wrapper for the
+ * implmentation-specific function slot_dealloc().
+
+ * @param keystore The Keystore object to operate on.
+ * @param[in] owner_id ID of the key owner.
+ * @param[in] slot If successful, assigned slot ID.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t keystore_slot_dealloc(fsl_shw_kso_t * keystore,
+ uint64_t owner_id, uint32_t slot);
+
+/*!
+ * Load cleartext key data into a key slot
+ *
+ * This function loads a key slot with cleartext data.
+ *
+ * @param keystore The Keystore object to operate on.
+ * @param[in] owner_id ID of the key owner.
+ * @param[in] slot If successful, assigned slot ID.
+ * @param[in] key_data Pointer to the location of the cleartext key data.
+ * @param[in] key_length Length of the key data (octets).
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t
+keystore_slot_load(fsl_shw_kso_t * keystore, uint64_t owner_id, uint32_t slot,
+ const uint8_t * key_data, uint32_t key_length);
+
+/*!
+ * Read cleartext key data from a key slot
+ *
+ * This function returns the key in a key slot.
+ *
+ * @param keystore The Keystore object to operate on.
+ * @param[in] owner_id ID of the key owner.
+ * @param[in] slot ID of slot where key resides.
+ * @param[in] key_length Length of the key data (octets).
+ * @param[out] key_data Pointer to the location of the cleartext key data.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t
+keystore_slot_read(fsl_shw_kso_t * keystore, uint64_t owner_id, uint32_t slot,
+ uint32_t key_length, uint8_t * key_data);
+
+/*!
+ * Encrypt a keyslot
+ *
+ * This function encrypts a key using the hardware secret key.
+ *
+ * @param user_ctx User context
+ * @param keystore The Keystore object to operate on.
+ * @param[in] owner_id ID of the key owner.
+ * @param[in] slot Slot ID of the key to encrypt.
+ * @param[in] length Length of the key
+ * @param[out] destination Pointer to the location where the encrypted data
+ * is to be stored.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t
+keystore_slot_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_kso_t * keystore, uint64_t owner_id,
+ uint32_t slot, uint32_t length, uint8_t * destination);
+
+/*!
+ * Decrypt a keyslot
+ *
+ * This function decrypts a key using the hardware secret key.
+ *
+ * @param user_ctx User context
+ * @param keystore The Keystore object to operate on.
+ * @param[in] owner_id ID of the key owner.
+ * @param[in] slot Slot ID of the key to encrypt.
+ * @param[in] length Length of the key
+ * @param[in] source Pointer to the location where the encrypted data
+ * is stored.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t
+keystore_slot_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_kso_t * keystore, uint64_t owner_id,
+ uint32_t slot, uint32_t length, const uint8_t * source);
+
+/* @} */
+
+/*! @addtogroup default_keystore
+ @{ */
+
+/*!
+ * Data structure to hold per-slot information
+ */
+typedef struct keystore_data_slot_info_t {
+ uint8_t allocated; /*!< Track slot assignments */
+ uint64_t owner; /*!< Owner IDs */
+ uint32_t key_length; /*!< Size of the key */
+} keystore_data_slot_info_t;
+
+/*!
+ * Data structure to hold keystore information.
+ */
+typedef struct keystore_data_t {
+ void *base_address; /*!< Base of the Secure Partition */
+ uint32_t slot_count; /*!< Number of slots in the keystore */
+ struct keystore_data_slot_info_t *slot; /*!< Per-slot information */
+} keystore_data_t;
+
+/*!
+ * Default keystore initialization routine.
+ *
+ * This function acquires a Secure Partition Object to store the keystore,
+ * divides it into slots of length #KEYSTORE_SLOT_SIZE, and builds a data
+ * structure to hold key information.
+ *
+ * @param user_ctx User context
+ * @param[out] user_data Pointer to the location where the keystore data
+ * structure is to be stored.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t shw_kso_init_data(fsl_shw_uco_t * user_ctx, void **user_data);
+
+/*!
+ * Default keystore cleanup routine.
+ *
+ * This function releases the Secure Partition Object and the memory holding
+ * the keystore data structure, that obtained by the shw_kso_init_data
+ * function.
+ *
+ * @param user_ctx User context
+ * @param[in,out] user_data Pointer to the location where the keystore data
+ * structure is stored.
+ */
+void shw_kso_cleanup_data(fsl_shw_uco_t * user_ctx, void **user_data);
+
+/*!
+ * Default keystore slot access verification
+ *
+ * This function compares the supplied Owner ID to the registered owner of
+ * the key slot, to see if the supplied ID is correct.
+ *
+ * @param[in] user_data Pointer to the location where the keystore data
+ * structure stored.
+ * @param[in] owner_id Owner ID supplied as a credential.
+ * @param[in] slot Requested slot
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t shw_slot_verify_access(void *user_data, uint64_t owner_id,
+ uint32_t slot);
+
+/*!
+ * Default keystore slot allocation
+ *
+ * This function first checks that the requested size is equal to or less than
+ * the maximum keystore slot size. If so, it searches the keystore for a free
+ * key slot, and if found, marks it as used and returns a slot reference to the
+ * user.
+ *
+ * @param[in] user_data Pointer to the location where the keystore data
+ * structure stored.
+ * @param[in] size Size of the key data that will be stored in this slot
+ * (octets)
+ * @param[in] owner_id Owner ID supplied as a credential.
+ * @param[out] slot Requested slot
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t shw_slot_alloc(void *user_data, uint32_t size,
+ uint64_t owner_id, uint32_t * slot);
+
+/*!
+ * Default keystore slot deallocation
+ *
+ * This function releases the given key slot in the keystore, making it
+ * available to store a new key.
+ *
+ * @param[in] user_data Pointer to the location where the keystore data
+ * structure stored.
+ * @param[in] owner_id Owner ID supplied as a credential.
+ * @param[in] slot Requested slot
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t shw_slot_dealloc(void *user_data,
+ uint64_t owner_id, uint32_t slot);
+
+/*!
+ * Default keystore slot address lookup
+ *
+ * This function calculates the address where the key data is stored.
+ *
+ * @param[in] user_data Pointer to the location where the keystore data
+ * structure stored.
+ * @param[in] slot Requested slot
+ *
+ * @return SCC2: Virtual address (kernel or userspace) of the key data.
+ * SCC: Physical address of the key data.
+ */
+void *shw_slot_get_address(void *user_data, uint32_t slot);
+
+/*!
+ * Default keystore slot base address lookup
+ *
+ * This function calculates the base address of the Secure Partition on which
+ * the key data is located. For the reference design, only one Secure
+ * Partition is used per Keystore, however in general, any number may be used.
+ *
+ * @param[in] user_data Pointer to the location where the keystore data
+ * structure stored.
+ * @param[in] slot Requested slot
+ *
+ * @return SCC2: Secure Partition virtual (kernel or userspace) base address.
+ * SCC: Secure Partition physical base address.
+ */
+uint32_t shw_slot_get_base(void *user_data, uint32_t slot);
+
+/*!
+ * Default keystore slot offset lookup
+ *
+ * This function calculates the offset from the base of the Secure Partition
+ * where the key data is located.
+ *
+ * @param[in] user_data Pointer to the location where the keystore data
+ * structure stored.
+ * @param[in] slot Requested slot
+ *
+ * @return SCC2: Key data offset (octets)
+ * SCC: Not implemented
+ */
+uint32_t shw_slot_get_offset(void *user_data, uint32_t slot);
+
+/*!
+ * Default keystore slot offset lookup
+ *
+ * This function returns the size of the given key slot. In the reference
+ * implementation, all key slots are of the same size, however in general,
+ * the keystore slot sizes can be made variable.
+ *
+ * @param[in] user_data Pointer to the location where the keystore data
+ * structure stored.
+ * @param[in] slot Requested slot
+ *
+ * @return SCC2: Keystore slot size.
+ * SCC: Not implemented
+ */
+uint32_t shw_slot_get_slot_size(void *user_data, uint32_t slot);
+
+/* @} */
+
+#endif /* FSL_SHW_KEYSTORE_H */
diff --git a/drivers/mxc/security/sahara2/include/linux_port.h b/drivers/mxc/security/sahara2/include/linux_port.h
new file mode 100644
index 000000000000..bd65c55c4f7b
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/linux_port.h
@@ -0,0 +1,1806 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file linux_port.h
+ *
+ * OS_PORT ported to Linux (2.6.9+ for now)
+ *
+ */
+
+ /*!
+ * @if USE_MAINPAGE
+ * @mainpage ==Linux version of== Generic OS API for STC Drivers
+ * @endif
+ *
+ * @section intro_sec Introduction
+ *
+ * This API / kernel programming environment blah blah.
+ *
+ * See @ref dkops "Driver-to-Kernel Operations" as a good place to start.
+ */
+
+#ifndef LINUX_PORT_H
+#define LINUX_PORT_H
+
+#define PORTABLE_OS_VERSION 101
+
+/* Linux Kernel Includes */
+#include <linux/version.h> /* Current version Linux kernel */
+
+#if defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+#include <linux/modversions.h>
+#endif
+#define MODVERSIONS
+#endif
+/*!
+ * __NO_VERSION__ defined due to Kernel module possibly spanning multiple
+ * files.
+ */
+#define __NO_VERSION__
+
+#include <linux/module.h> /* Basic support for loadable modules,
+ printk */
+#include <linux/init.h> /* module_init, module_exit */
+#include <linux/kernel.h> /* General kernel system calls */
+#include <linux/sched.h> /* for interrupt.h */
+#include <linux/fs.h> /* for inode */
+#include <linux/random.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h> /* kmalloc */
+
+#include <stdarg.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+#include <linux/device.h> /* used in dynamic power management */
+#else
+#include <linux/platform_device.h> /* used in dynamic power management */
+#endif
+
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/clk.h> /* clock en/disable for DPM */
+
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/uaccess.h> /* copy_to_user(), copy_from_user() */
+#include <asm/io.h> /* ioremap() */
+#include <asm/irq.h>
+#include <asm/cacheflush.h>
+
+#include <mach/hardware.h>
+
+#ifndef TRUE
+/*! Useful symbol for unsigned values used as flags. */
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+/*! Useful symbol for unsigned values used as flags. */
+#define FALSE 0
+#endif
+
+/* These symbols are defined in Linux 2.6 and later. Include here for minimal
+ * support of 2.4 kernel.
+ **/
+#if !defined(LINUX_VERSION_CODE) || LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+/*!
+ * Symbol defined somewhere in 2.5/2.6. It is the return signature of an ISR.
+ */
+#define irqreturn_t void
+/*! Possible return value of 'modern' ISR routine. */
+#define IRQ_HANDLED
+/*! Method of generating value of 'modern' ISR routine. */
+#define IRQ_RETVAL(x)
+#endif
+
+/*!
+ * Type used for registering and deregistering interrupts.
+ */
+typedef int os_interrupt_id_t;
+
+/*!
+ * Type used as handle for a process
+ *
+ * See #os_get_process_handle() and #os_send_signal().
+ */
+/*
+ * The following should be defined this way, but it gets compiler errors
+ * on the current tool chain.
+ *
+ * typedef task_t *os_process_handle_t;
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+typedef task_t *os_process_handle_t;
+#else
+typedef struct task_struct *os_process_handle_t;
+#endif
+
+/*!
+ * Generic return code for functions which need such a thing.
+ *
+ * No knowledge should be assumed of the value of any of these symbols except
+ * that @c OS_ERROR_OK_S is guaranteed to be zero.
+ */
+typedef enum {
+ OS_ERROR_OK_S = 0, /*!< Success */
+ OS_ERROR_FAIL_S = -EIO, /*!< Generic driver failure */
+ OS_ERROR_NO_MEMORY_S = -ENOMEM, /*!< Failure to acquire/use memory */
+ OS_ERROR_BAD_ADDRESS_S = -EFAULT, /*!< Bad address */
+ OS_ERROR_BAD_ARG_S = -EINVAL, /*!< Bad input argument */
+} os_error_code;
+
+/*!
+ * Handle to a lock.
+ */
+#ifdef CONFIG_PREEMPT_RT
+typedef raw_spinlock_t *os_lock_t;
+#else
+typedef spinlock_t *os_lock_t;
+#endif
+
+/*!
+ * Context while locking.
+ */
+typedef unsigned long os_lock_context_t;
+
+/*!
+ * Declare a wait object for sleeping/waking processes.
+ */
+#define OS_WAIT_OBJECT(name) \
+ DECLARE_WAIT_QUEUE_HEAD(name##_qh)
+
+/*!
+ * Driver registration handle
+ *
+ * Used with #os_driver_init_registration(), #os_driver_add_registration(),
+ * and #os_driver_complete_registration().
+ */
+typedef struct {
+ unsigned reg_complete; /*!< TRUE if next inits succeeded. */
+ dev_t dev; /*!< dev_t for register_chrdev() */
+ struct file_operations fops; /*!< struct for register_chrdev() */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
+ struct class_simple *cs; /*!< results of class_simple_create() */
+#else
+ struct class *cs; /*!< results of class_create() */
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+ struct class_device *cd; /*!< Result of class_device_create() */
+#else
+ struct device *cd; /*!< Result of device_create() */
+#endif
+ unsigned power_complete; /*!< TRUE if next inits succeeded */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ struct device_driver dd; /*!< struct for register_driver() */
+#else
+ struct platform_driver dd; /*!< struct for register_driver() */
+#endif
+ struct platform_device pd; /*!< struct for platform_register_device() */
+} os_driver_reg_t;
+
+/*
+ * Function types which can be associated with driver entry points.
+ *
+ * Note that init and shutdown are absent.
+ */
+/*! @{ */
+/*! Keyword for registering open() operation handler. */
+#define OS_FN_OPEN open
+/*! Keyword for registering close() operation handler. */
+#define OS_FN_CLOSE release
+/*! Keyword for registering read() operation handler. */
+#define OS_FN_READ read
+/*! Keyword for registering write() operation handler. */
+#define OS_FN_WRITE write
+/*! Keyword for registering ioctl() operation handler. */
+#define OS_FN_IOCTL ioctl
+/*! Keyword for registering mmap() operation handler. */
+#define OS_FN_MMAP mmap
+/*! @} */
+
+/*!
+ * Function signature for the portable interrupt handler
+ *
+ * While it would be nice to know which interrupt is being serviced, the
+ * Least Common Denominator rule says that no arguments get passed in.
+ *
+ * @return Zero if not handled, non-zero if handled.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+typedef int (*os_interrupt_handler_t) (int, void *, struct pt_regs *);
+#else
+typedef int (*os_interrupt_handler_t) (int, void *);
+#endif
+
+/*!
+ * @defgroup dkops Driver-to-Kernel Operations
+ *
+ * These are the operations which drivers should call to get the OS to perform
+ * services.
+ */
+
+/*! @addtogroup dkops */
+/*! @{ */
+
+/*!
+ * Register an interrupt handler.
+ *
+ * @param driver_name The name of the driver
+ * @param interrupt_id The interrupt line to monitor (type
+ * #os_interrupt_id_t)
+ * @param function The function to be called to handle an interrupt
+ *
+ * @return #os_error_code
+ */
+#define os_register_interrupt(driver_name, interrupt_id, function) \
+ request_irq(interrupt_id, function, 0, driver_name, NULL)
+
+/*!
+ * Deregister an interrupt handler.
+ *
+ * @param interrupt_id The interrupt line to stop monitoring
+ *
+ * @return #os_error_code
+ */
+#define os_deregister_interrupt(interrupt_id) \
+ free_irq(interrupt_id, NULL)
+
+/*!
+ * INTERNAL implementation of os_driver_init_registration()
+ *
+ * @return An os error code.
+ */
+inline static int os_drv_do_init_reg(os_driver_reg_t * handle)
+{
+ memset(handle, 0, sizeof(*handle));
+ handle->fops.owner = THIS_MODULE;
+ handle->power_complete = FALSE;
+ handle->reg_complete = FALSE;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ handle->dd.name = NULL;
+#else
+ handle->dd.driver.name = NULL;
+#endif
+
+ return OS_ERROR_OK_S;
+}
+
+/*!
+ * Initialize driver registration.
+ *
+ * If the driver handles open(), close(), ioctl(), read(), write(), or mmap()
+ * calls, then it needs to register their location with the kernel so that they
+ * get associated with the device.
+ *
+ * @param handle The handle object to be used with this registration. The
+ * object must live (be in memory somewhere) at least until
+ * os_driver_remove_registration() is called.
+ *
+ * @return A handle for further driver registration, or NULL if failed.
+ */
+#define os_driver_init_registration(handle) \
+ os_drv_do_init_reg(&handle)
+
+/*!
+ * Add a function registration to driver registration.
+ *
+ * @param handle A handle initialized by #os_driver_init_registration().
+ * @param name Which function is being supported.
+ * @param function The result of a call to a @c _REF version of one of the
+ * driver function signature macros
+ * @return void
+ */
+#define os_driver_add_registration(handle, name, function) \
+ do {handle.fops.name = (void*)(function); } while (0)
+
+/*!
+ * Record 'power suspend' function for the device.
+ *
+ * @param handle A handle initialized by #os_driver_init_registration().
+ * @param function Name of function to call on power suspend request
+ *
+ * Status: Provisional
+ *
+ * @return void
+ */
+#define os_driver_register_power_suspend(handle, function) \
+ handle.dd.suspend = function
+
+/*!
+ * Record 'power resume' function for the device.
+ *
+ * @param handle A handle initialized by #os_driver_init_registration().
+ * @param function Name of function to call on power resume request
+ *
+ * Status: Provisional
+ *
+ * @return void
+ */
+#define os_driver_register_resume(handle, function) \
+ handle.dd.resume = function
+
+/*!
+ * INTERNAL function of the Linux port of the OS API. Implements the
+ * os_driver_complete_registration() function.
+ *
+ * @param handle The handle used with #os_driver_init_registration().
+ * @param major The major device number to be associated with the driver.
+ * If this value is zero, a major number may be assigned.
+ * See #os_driver_get_major() to determine final value.
+ * #os_driver_remove_registration().
+ * @param driver_name The driver name. Can be used as part of 'device node'
+ * name on platforms which support such a feature.
+ *
+ * @return An error code
+ */
+inline static int os_drv_do_reg(os_driver_reg_t * handle,
+ unsigned major, char *driver_name)
+{
+ os_error_code code = OS_ERROR_NO_MEMORY_S;
+ char *name = kmalloc(strlen(driver_name) + 1, 0);
+
+ if (name != NULL) {
+ memcpy(name, driver_name, strlen(driver_name) + 1);
+ code = OS_ERROR_OK_S; /* OK so far */
+ /* If any chardev/POSIX routines were added, then do chrdev part */
+ if (handle->fops.open || handle->fops.release
+ || handle->fops.read || handle->fops.write
+ || handle->fops.ioctl || handle->fops.mmap) {
+
+ printk("ioctl pointer: %p. mmap pointer: %p\n",
+ handle->fops.ioctl, handle->fops.mmap);
+
+ /* this method is depricated, see:
+ * http://lwn.net/Articles/126808/
+ */
+ code =
+ register_chrdev(major, driver_name, &handle->fops);
+
+ /* instead something like this: */
+#if 0
+ handle->dev = MKDEV(major, 0);
+ code =
+ register_chrdev_region(handle->dev, 1, driver_name);
+ if (code < 0) {
+ code = OS_ERROR_FAIL_S;
+ } else {
+ cdev_init(&handle->cdev, &handle->fops);
+ code = cdev_add(&handle->cdev, major, 1);
+ }
+#endif
+
+ if (code < 0) {
+ code = OS_ERROR_FAIL_S;
+ } else {
+ if (code != 0) {
+ /* Zero was passed in for major; code is actual value */
+ handle->dev = MKDEV(code, 0);
+ } else {
+ handle->dev = MKDEV(major, 0);
+ }
+ code = OS_ERROR_OK_S;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
+ handle->cs =
+ class_simple_create(THIS_MODULE,
+ driver_name);
+ if (IS_ERR(handle->cs)) {
+ code = (os_error_code) handle->cs;
+ handle->cs = NULL;
+ } else {
+ handle->cd =
+ class_simple_device_add(handle->cs,
+ handle->dev,
+ NULL,
+ driver_name);
+ if (IS_ERR(handle->cd)) {
+ class_simple_device_remove
+ (handle->dev);
+ unregister_chrdev(MAJOR
+ (handle->dev),
+ driver_name);
+ code =
+ (os_error_code) handle->cs;
+ handle->cs = NULL;
+ } else {
+ handle->reg_complete = TRUE;
+ }
+ }
+#else
+ handle->cs =
+ class_create(THIS_MODULE, driver_name);
+ if (IS_ERR(handle->cs)) {
+ code = (os_error_code) handle->cs;
+ handle->cs = NULL;
+ } else {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+ handle->cd =
+ class_device_create(handle->cs,
+ NULL,
+ handle->dev,
+ NULL,
+ driver_name);
+#else
+ handle->cd =
+ device_create(handle->cs, NULL,
+ handle->dev, NULL,
+ driver_name);
+#endif
+ if (IS_ERR(handle->cd)) {
+ class_destroy(handle->cs);
+ unregister_chrdev(MAJOR
+ (handle->dev),
+ driver_name);
+ code =
+ (os_error_code) handle->cs;
+ handle->cs = NULL;
+ } else {
+ handle->reg_complete = TRUE;
+ }
+ }
+#endif
+ }
+ }
+ /* ... fops routine registered */
+ /* Handle power management fns through separate interface */
+ if ((code == OS_ERROR_OK_S) &&
+ (handle->dd.suspend || handle->dd.resume)) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ handle->dd.name = name;
+ handle->dd.bus = &platform_bus_type;
+ code = driver_register(&handle->dd);
+#else
+ handle->dd.driver.name = name;
+ handle->dd.driver.bus = &platform_bus_type;
+ code = driver_register(&handle->dd.driver);
+#endif
+ if (code == OS_ERROR_OK_S) {
+ handle->pd.name = name;
+ handle->pd.id = 0;
+ code = platform_device_register(&handle->pd);
+ if (code != OS_ERROR_OK_S) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ driver_unregister(&handle->dd);
+#else
+ driver_unregister(&handle->dd.driver);
+#endif
+ } else {
+ handle->power_complete = TRUE;
+ }
+ }
+ } /* ... suspend or resume */
+ } /* name != NULL */
+ return code;
+}
+
+/*!
+ * Finalize the driver registration with the kernel.
+ *
+ * Upon return from this call, the driver may begin receiving calls at the
+ * defined entry points.
+ *
+ * @param handle The handle used with #os_driver_init_registration().
+ * @param major The major device number to be associated with the driver.
+ * If this value is zero, a major number may be assigned.
+ * See #os_driver_get_major() to determine final value.
+ * #os_driver_remove_registration().
+ * @param driver_name The driver name. Can be used as part of 'device node'
+ * name on platforms which support such a feature.
+ *
+ * @return An error code
+ */
+#define os_driver_complete_registration(handle, major, driver_name) \
+ os_drv_do_reg(&handle, major, driver_name)
+
+/*!
+ * Get driver Major Number from handle after a successful registration.
+ *
+ * @param handle A handle which has completed registration.
+ *
+ * @return The major number (if any) associated with the handle.
+ */
+#define os_driver_get_major(handle) \
+ (handle.reg_complete ? MAJOR(handle.dev) : -1)
+
+/*!
+ * INTERNAL implemention of os_driver_remove_registration.
+ *
+ * @param handle A handle initialized by #os_driver_init_registration().
+ *
+ * @return An error code.
+ */
+inline static int os_drv_rmv_reg(os_driver_reg_t * handle)
+{
+ if (handle->reg_complete) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
+ if (handle->cd != NULL) {
+ class_simple_device_remove(handle->dev);
+ handle->cd = NULL;
+ }
+ if (handle->cs != NULL) {
+ class_simple_destroy(handle->cs);
+ handle->cs = NULL;
+ }
+ unregister_chrdev(MAJOR(handle->dev), handle->dd.name);
+#else
+ if (handle->cd != NULL) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+ class_device_destroy(handle->cs, handle->dev);
+#else
+ device_destroy(handle->cs, handle->dev);
+#endif
+ handle->cd = NULL;
+ }
+ if (handle->cs != NULL) {
+ class_destroy(handle->cs);
+ handle->cs = NULL;
+ }
+ unregister_chrdev(MAJOR(handle->dev), handle->dd.driver.name);
+#endif
+ handle->reg_complete = FALSE;
+ }
+ if (handle->power_complete) {
+ platform_device_unregister(&handle->pd);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ driver_unregister(&handle->dd);
+#else
+ driver_unregister(&handle->dd.driver);
+#endif
+ handle->power_complete = FALSE;
+ }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ if (handle->dd.name != NULL) {
+ kfree(handle->dd.name);
+ handle->dd.name = NULL;
+ }
+#else
+ if (handle->dd.driver.name != NULL) {
+ kfree(handle->dd.driver.name);
+ handle->dd.driver.name = NULL;
+ }
+#endif
+ return OS_ERROR_OK_S;
+}
+
+/*!
+ * Remove the driver's registration with the kernel.
+ *
+ * Upon return from this call, the driver not receive any more calls at the
+ * defined entry points (other than ISR and shutdown).
+ *
+ * @param handle A handle initialized by #os_driver_init_registration().
+ *
+ * @return An error code.
+ */
+#define os_driver_remove_registration(handle) \
+ os_drv_rmv_reg(&handle)
+
+/*!
+ * Register a driver with the Linux Device Model.
+ *
+ * @param driver_information The device_driver structure information
+ *
+ * @return An error code.
+ *
+ * Status: denigrated in favor of #os_driver_complete_registration()
+ */
+#define os_register_to_driver(driver_information) \
+ driver_register(driver_information)
+
+/*!
+ * Unregister a driver from the Linux Device Model
+ *
+ * this routine unregisters from the Linux Device Model
+ *
+ * @param driver_information The device_driver structure information
+ *
+ * @return An error code.
+ *
+ * Status: Denigrated. See #os_register_to_driver().
+ */
+#define os_unregister_from_driver(driver_information) \
+ driver_unregister(driver_information)
+
+/*!
+ * register a device to a driver
+ *
+ * this routine registers a drivers devices to the Linux Device Model
+ *
+ * @param device_information The platform_device structure information
+ *
+ * @return An error code.
+ *
+ * Status: denigrated in favor of #os_driver_complete_registration()
+ */
+#define os_register_a_device(device_information) \
+ platform_device_register(device_information)
+
+/*!
+ * unregister a device from a driver
+ *
+ * this routine unregisters a drivers devices from the Linux Device Model
+ *
+ * @param device_information The platform_device structure information
+ *
+ * @return An error code.
+ *
+ * Status: Denigrated. See #os_register_a_device().
+ */
+#define os_unregister_a_device(device_information) \
+ platform_device_unregister(device_information)
+
+/*!
+ * Print a message to console / into log file. After the @c msg argument a
+ * number of printf-style arguments may be added. Types should be limited to
+ * printf string, char, octal, decimal, and hexadecimal types. (This excludes
+ * pointers, and floating point).
+ *
+ * @param msg The main text of the message to be logged
+ * @param s The printf-style arguments which go with msg, if any
+ *
+ * @return (void)
+ */
+#define os_printk(...) \
+ (void) printk(__VA_ARGS__)
+
+/*!
+ * Prepare a task to execute the given function. This should only be done once
+ * per function,, during the driver's initialization routine.
+ *
+ * @param task_fn Name of the OS_DEV_TASK() function to be created.
+ *
+ * @return an OS ERROR code.
+ */
+#define os_create_task(function_name) \
+ OS_ERROR_OK_S
+
+/*!
+ * Schedule execution of a task.
+ *
+ * @param function_name The function associated with the task.
+ *
+ * @return (void)
+ */
+#define os_dev_schedule_task(function_name) \
+ tasklet_schedule(&(function_name ## let))
+
+/*!
+ * Make sure that task is no longer running and will no longer run.
+ *
+ * This function will not return until both are true. This is useful when
+ * shutting down a driver.
+ */
+#define os_dev_stop_task(function_name) \
+do { \
+ tasklet_disable(&(function_name ## let)); \
+ tasklet_kill(&(function_name ## let)); \
+} while (0)
+
+/*!
+ * Allocate some kernel memory
+ *
+ * @param amount Number of 8-bit bytes to allocate
+ * @param flags Some indication of purpose of memory (needs definition)
+ *
+ * @return Pointer to allocated memory, or NULL if failed.
+ */
+#define os_alloc_memory(amount, flags) \
+ (void*)kmalloc(amount, flags)
+
+/*!
+ * Free some kernel memory
+ *
+ * @param location The beginning of the region to be freed.
+ *
+ * Do some OSes have separate free() functions which should be
+ * distinguished by passing in @c flags here, too? Don't some also require the
+ * size of the buffer being freed?
+ */
+#define os_free_memory(location) \
+ kfree(location)
+
+/*!
+ * Allocate cache-coherent memory
+ *
+ * @param amount Number of bytes to allocate
+ * @param[out] dma_addrp Location to store physical address of allocated
+ * memory.
+ * @param flags Some indication of purpose of memory (needs
+ * definition).
+ *
+ * @return (virtual space) pointer to allocated memory, or NULL if failed.
+ *
+ */
+#define os_alloc_coherent(amount, dma_addrp, flags) \
+ (void*)dma_alloc_coherent(NULL, amount, dma_addrp, flags)
+
+/*!
+ * Free cache-coherent memory
+ *
+ * @param size Number of bytes which were allocated.
+ * @param virt_addr Virtual(kernel) address of memory.to be freed, as
+ * returned by #os_alloc_coherent().
+ * @param dma_addr Physical address of memory.to be freed, as returned
+ * by #os_alloc_coherent().
+ *
+ * @return void
+ *
+ */
+#define os_free_coherent(size, virt_addr, dma_addr) \
+ dma_free_coherent(NULL, size, virt_addr, dma_addr
+
+/*!
+ * Map an I/O space into kernel memory space
+ *
+ * @param start The starting address of the (physical / io space) region
+ * @param range_bytes The number of bytes to map
+ *
+ * @return A pointer to the mapped area, or NULL on failure
+ */
+#define os_map_device(start, range_bytes) \
+ (void*)ioremap_nocache((start), range_bytes)
+
+/*!
+ * Unmap an I/O space from kernel memory space
+ *
+ * @param start The starting address of the (virtual) region
+ * @param range_bytes The number of bytes to unmap
+ *
+ * @return None
+ */
+#define os_unmap_device(start, range_bytes) \
+ iounmap((void*)(start))
+
+/*!
+ * Copy data from Kernel space to User space
+ *
+ * @param to The target location in user memory
+ * @param from The source location in kernel memory
+ * @param size The number of bytes to be copied
+ *
+ * @return #os_error_code
+ */
+#define os_copy_to_user(to, from, size) \
+ ((copy_to_user(to, from, size) == 0) ? 0 : OS_ERROR_BAD_ADDRESS_S)
+
+/*!
+ * Copy data from User space to Kernel space
+ *
+ * @param to The target location in kernel memory
+ * @param from The source location in user memory
+ * @param size The number of bytes to be copied
+ *
+ * @return #os_error_code
+ */
+#define os_copy_from_user(to, from, size) \
+ ((copy_from_user(to, from, size) == 0) ? 0 : OS_ERROR_BAD_ADDRESS_S)
+
+/*!
+ * Read a 8-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @return The value in the register
+ */
+#define os_read8(register_address) \
+ __raw_readb(register_address)
+
+/*!
+ * Write a 8-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @param value The value to write into the register
+ */
+#define os_write8(register_address, value) \
+ __raw_writeb(value, register_address)
+
+/*!
+ * Read a 16-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @return The value in the register
+ */
+#define os_read16(register_address) \
+ __raw_readw(register_address)
+
+/*!
+ * Write a 16-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @param value The value to write into the register
+ */
+#define os_write16(register_address, value) \
+ __raw_writew(value, (uint32_t*)(register_address))
+
+/*!
+ * Read a 32-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @return The value in the register
+ */
+#define os_read32(register_address) \
+ __raw_readl((uint32_t*)(register_address))
+
+/*!
+ * Write a 32-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @param value The value to write into the register
+ */
+#define os_write32(register_address, value) \
+ __raw_writel(value, register_address)
+
+/*!
+ * Read a 64-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @return The value in the register
+ */
+#define os_read64(register_address) \
+ ERROR_UNIMPLEMENTED
+
+/*!
+ * Write a 64-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @param value The value to write into the register
+ */
+#define os_write64(register_address, value) \
+ ERROR_UNIMPLEMENTED
+
+/*!
+ * Delay some number of microseconds
+ *
+ * Note that this is a busy-loop, not a suspension of the task/process.
+ *
+ * @param msecs The number of microseconds to delay
+ *
+ * @return void
+ */
+#define os_mdelay mdelay
+
+/*!
+ * Calculate virtual address from physical address
+ *
+ * @param pa Physical address
+ *
+ * @return virtual address
+ *
+ * @note this assumes that addresses are 32 bits wide
+ */
+#define os_va __va
+
+/*!
+ * Calculate physical address from virtual address
+ *
+ *
+ * @param va Virtual address
+ *
+ * @return physical address
+ *
+ * @note this assumes that addresses are 32 bits wide
+ */
+#define os_pa __pa
+
+#ifdef CONFIG_PREEMPT_RT
+/*!
+ * Allocate and initialize a lock, returning a lock handle.
+ *
+ * The lock state will be initialized to 'unlocked'.
+ *
+ * @return A lock handle, or NULL if an error occurred.
+ */
+inline static os_lock_t os_lock_alloc_init(void)
+{
+ raw_spinlock_t *lockp;
+ lockp = (raw_spinlock_t *) kmalloc(sizeof(raw_spinlock_t), 0);
+ if (lockp) {
+ _raw_spin_lock_init(lockp);
+ } else {
+ printk("OS: lock init failed\n");
+ }
+
+ return lockp;
+}
+#else
+/*!
+ * Allocate and initialize a lock, returning a lock handle.
+ *
+ * The lock state will be initialized to 'unlocked'.
+ *
+ * @return A lock handle, or NULL if an error occurred.
+ */
+inline static os_lock_t os_lock_alloc_init(void)
+{
+ spinlock_t *lockp;
+ lockp = (spinlock_t *) kmalloc(sizeof(spinlock_t), 0);
+ if (lockp) {
+ spin_lock_init(lockp);
+ } else {
+ printk("OS: lock init failed\n");
+ }
+
+ return lockp;
+}
+#endif /* CONFIG_PREEMPT_RT */
+
+/*!
+ * Acquire a lock.
+ *
+ * This function should only be called from an interrupt service routine.
+ *
+ * @param lock_handle A handle to the lock to acquire.
+ *
+ * @return void
+ */
+#define os_lock(lock_handle) \
+ spin_lock(lock_handle)
+
+/*!
+ * Unlock a lock. Lock must have been acquired by #os_lock().
+ *
+ * @param lock_handle A handle to the lock to unlock.
+ *
+ * @return void
+ */
+#define os_unlock(lock_handle) \
+ spin_unlock(lock_handle)
+
+/*!
+ * Acquire a lock in non-ISR context
+ *
+ * This function will spin until the lock is available.
+ *
+ * @param lock_handle A handle of the lock to acquire.
+ * @param context Place to save the before-lock context
+ *
+ * @return void
+ */
+#define os_lock_save_context(lock_handle, context) \
+ spin_lock_irqsave(lock_handle, context)
+
+/*!
+ * Release a lock in non-ISR context
+ *
+ * @param lock_handle A handle of the lock to release.
+ * @param context Place where before-lock context was saved.
+ *
+ * @return void
+ */
+#define os_unlock_restore_context(lock_handle, context) \
+ spin_unlock_irqrestore(lock_handle, context)
+
+/*!
+ * Deallocate a lock handle.
+ *
+ * @param lock_handle An #os_lock_t that has been allocated.
+ *
+ * @return void
+ */
+#define os_lock_deallocate(lock_handle) \
+ kfree(lock_handle)
+
+/*!
+ * Determine process handle
+ *
+ * The process handle of the current user is returned.
+ *
+ * @return A handle on the current process.
+ */
+#define os_get_process_handle() \
+ current
+
+/*!
+ * Send a signal to a process
+ *
+ * @param proc A handle to the target process.
+ * @param sig The POSIX signal to send to that process.
+ */
+#define os_send_signal(proc, sig) \
+ send_sig(sig, proc, 0);
+
+/*!
+ * Get some random bytes
+ *
+ * @param buf The location to store the random data.
+ * @param count The number of bytes to store.
+ *
+ * @return void
+ */
+#define os_get_random_bytes(buf, count) \
+ get_random_bytes(buf, count)
+
+/*!
+ * Go to sleep on an object.
+ *
+ * @param object The object on which to sleep
+ * @param condition An expression to check for sleep completion. Must be
+ * coded so that it can be referenced more than once inside
+ * macro, i.e., no ++ or other modifying expressions.
+ * @param atomic Non-zero if sleep must not return until condition.
+ *
+ * @return error code -- OK or sleep interrupted??
+ */
+#define os_sleep(object, condition, atomic) \
+({ \
+ DEFINE_WAIT(_waitentry_); \
+ os_error_code code = OS_ERROR_OK_S; \
+ \
+ while (!(condition)) { \
+ prepare_to_wait(&(object##_qh), &_waitentry_, \
+ atomic ? 0 : TASK_INTERRUPTIBLE); \
+ if (!(condition)) { \
+ schedule(); \
+ } \
+ \
+ finish_wait(&(object##_qh), &_waitentry_); \
+ \
+ if (!atomic && signal_pending(current)) { \
+ code = OS_ERROR_FAIL_S; /* NEED SOMETHING BETTER */ \
+ break; \
+ } \
+ }; \
+ \
+ code; \
+})
+
+/*!
+ * Wake up whatever is sleeping on sleep object
+ *
+ * @param object The object on which things might be sleeping
+ *
+ * @return none
+ */
+#define os_wake_sleepers(object) \
+ wake_up_interruptible(&(object##_qh));
+
+ /*! @} *//* dkops */
+
+/******************************************************************************
+ * Function signature-generating macros
+ *****************************************************************************/
+
+/*!
+ * @defgroup drsigs Driver Signatures
+ *
+ * These macros will define the entry point signatures for interrupt handlers;
+ * driver initialization and shutdown; device open/close; etc.
+ *
+ * There are two versions of each macro for a given Driver Entry Point. The
+ * first version is used to define a function and its implementation in the
+ * driver.c file, e.g. #OS_DEV_INIT().
+ *
+ * The second form is used whenever a forward declaration (prototype) is
+ * needed. It has the letters @c _DCL appended to the name of the defintion
+ * function, and takes only the first two arguments (driver_name and
+ * function_name). These are not otherwise mentioned in this documenation.
+ *
+ * There is a third form used when a reference to a function is required, for
+ * instance when passing the routine as a pointer to a function. It has the
+ * letters @c _REF appended to it, and takes only the first two arguments
+ * (driver_name and function_name). These functions are not otherwise
+ * mentioned in this documentation.
+ *
+ * (Note that these two extra forms are required because of the
+ * possibility/likelihood of having a 'wrapper function' which invokes the
+ * generic function with expected arguments. An alternative would be to have a
+ * generic function which isn't able to get at any arguments directly, but
+ * would be equipped with macros which could get at information passed in.
+ *
+ * Example:
+ *
+ * (in a header file)
+ * @code
+ * OS_DEV_INIT_DCL(widget, widget_init);
+ * @endcode
+ *
+ * (in an implementation file)
+ * @code
+ * OS_DEV_INIT(widget, widget_init)
+ * {
+ * os_dev_init_return(TRUE);
+ * }
+ * @endcode
+ *
+ */
+
+/*! @addtogroup drsigs */
+/*! @{ */
+
+/*!
+ * Define a function which will handle device initialization
+ *
+ * This is tne driver initialization routine. This is normally where the
+ * part would be initialized; queues, locks, interrupts handlers defined;
+ * long-term dynamic memory allocated for driver use; etc.
+ *
+ * @param function_name The name of the portable initialization function.
+ *
+ * @return A call to #os_dev_init_return()
+ *
+ */
+#define OS_DEV_INIT(function_name) \
+module_init(function_name); \
+static int __init function_name (void)
+
+/*! Make declaration for driver init function.
+ * @param function_name foo
+ */
+#define OS_DEV_INIT_DCL(function_name) \
+static int __init function_name (void);
+
+/*!
+ * Generate a function reference to the driver's init function.
+ * @param function_name Name of the OS_DEV_INIT() function.
+ *
+ * @return A function pointer.
+ */
+#define OS_DEV_INIT_REF(function_name) \
+function_name
+
+/*!
+ * Define a function which will handle device shutdown
+ *
+ * This is the inverse of the #OS_DEV_INIT() routine.
+ *
+ * @param function_name The name of the portable driver shutdown routine.
+ *
+ * @return A call to #os_dev_shutdown_return()
+ *
+ */
+#define OS_DEV_SHUTDOWN(function_name) \
+module_exit(function_name); \
+static void function_name(void)
+
+/*!
+ * Generate a function reference to the driver's shutdown function.
+ * @param function_name Name of the OS_DEV_HUSTDOWN() function.
+ *
+ * @return A function pointer.
+ */
+#define OS_DEV_SHUTDOWN_DCL(function_name) \
+static void function_name(void);
+
+/*!
+ * Generate a reference to driver's shutdown function
+ * @param function_name Name of the OS_DEV_HUSTDOWN() function.
+*/
+
+#define OS_DEV_SHUTDOWN_REF(function_name) \
+function_name
+
+/*!
+ * Define a function which will open the device for a user.
+ *
+ * @param function_name The name of the driver open() function
+ *
+ * @return A call to #os_dev_open_return()
+ */
+#define OS_DEV_OPEN(function_name) \
+static int function_name(struct inode* inode_p_, struct file* file_p_)
+
+/*!
+ * Declare prototype for an open() function.
+ *
+ * @param function_name The name of the OS_DEV_OPEN() function.
+ */
+#define OS_DEV_OPEN_DCL(function_name) \
+OS_DEV_OPEN(function_name);
+
+/*!
+ * Generate a function reference to the driver's open() function.
+ * @param function_name Name of the OS_DEV_OPEN() function.
+ *
+ * @return A function pointer.
+ */
+#define OS_DEV_OPEN_REF(function_name) \
+function_name
+
+/*!
+ * Define a function which will handle a user's ioctl() request
+ *
+ * @param function_name The name of the driver ioctl() function
+ *
+ * @return A call to #os_dev_ioctl_return()
+ */
+#define OS_DEV_IOCTL(function_name) \
+static int function_name(struct inode* inode_p_, struct file* file_p_, \
+ unsigned int cmd_, unsigned long data_)
+
+/*! Boo. */
+#define OS_DEV_IOCTL_DCL(function_name) \
+OS_DEV_IOCTL(function_name);
+
+/*!
+ * Generate a function reference to the driver's ioctl() function.
+ * @param function_name Name of the OS_DEV_IOCTL() function.
+ *
+ * @return A function pointer.
+ */
+#define OS_DEV_IOCTL_REF(function_name) \
+function_name
+
+/*!
+ * Define a function which will handle a user's mmap() request
+ *
+ * @param function_name The name of the driver mmap() function
+ *
+ * @return A call to #os_dev_ioctl_return()
+ */
+#define OS_DEV_MMAP(function_name) \
+int function_name(struct file* file_p_, struct vm_area_struct* vma_)
+
+#define OS_DEV_MMAP_DCL(function_name) \
+OS_DEV_MMAP(function_name);
+
+#define OS_DEV_MMAP_REF(function_name) \
+function_name
+
+/* Retrieve the context to the memory structure that is to be MMAPed */
+#define os_mmap_memory_ctx() (vma_)
+
+/* Determine the size of the requested MMAP region*/
+#define os_mmap_memory_size() (vma_->vm_end - vma_->vm_start)
+
+/* Determine the base address of the requested MMAP region*/
+#define os_mmap_user_base() (vma_->vm_start)
+
+/*!
+ * Declare prototype for an read() function.
+ *
+ * @param function_name The name of the driver read function.
+ */
+#define OS_DEV_READ_DCL(function_name) \
+OS_DEV_READ(function_name);
+
+/*!
+ * Generate a function reference to the driver's read() routine
+ * @param function_name Name of the OS_DEV_READ() function.
+ *
+ * @return A function pointer.
+ */
+#define OS_DEV_READ_REF(function_name) \
+function_name
+
+/*!
+ * Define a function which will handle a user's write() request
+ *
+ * @param function_name The name of the driver write() function
+ *
+ * @return A call to #os_dev_write_return()
+ */
+#define OS_DEV_WRITE(function_name) \
+static ssize_t function_name(struct file* file_p_, char* user_buffer_, \
+ size_t count_bytes_, loff_t* file_position_)
+
+/*!
+ * Declare prototype for an write() function.
+ *
+ * @param function_name The name of the driver write function.
+ */
+#define OS_DEV_WRITE_DCL(function_name) \
+OS_DEV_WRITE(function_name);
+
+/*!
+ * Generate a function reference to the driver's write() routine
+ * @param function_name Name of the OS_DEV_WRITE() function.
+ *
+ * @return A function pointer.
+ */
+#define OS_DEV_WRITE_REF(function_name) \
+function_name
+
+/*!
+ * Define a function which will close the device - opposite of OS_DEV_OPEN()
+ *
+ * @param function_name The name of the driver close() function
+ *
+ * @return A call to #os_dev_close_return()
+ */
+#define OS_DEV_CLOSE(function_name) \
+static int function_name(struct inode* inode_p_, struct file* file_p_)
+
+/*!
+ * Declare prototype for an close() function
+ *
+ * @param function_name The name of the driver close() function.
+ */
+#define OS_DEV_CLOSE_DCL(function_name) \
+OS_DEV_CLOSE(function_name);
+
+/*!
+ * Generate a function reference to the driver's close function.
+ * @param function_name Name of the OS_DEV_CLOSE() function.
+ *
+ * @return A function pointer.
+ */
+#define OS_DEV_CLOSE_REF(function_name) \
+function_name
+
+/*!
+ * Define a function which will handle an interrupt
+ *
+ * No arguments are available to the generic function. It must not invoke any
+ * OS functions which are illegal in a ISR. It gets no parameters, and must
+ * have a call to #os_dev_isr_return() instead of any/all return statements.
+ *
+ * Example:
+ * @code
+ * OS_DEV_ISR(widget)
+ * {
+ * os_dev_isr_return(1);
+ * }
+ * @endcode
+ *
+ * @param function_name The name of the driver ISR function
+ *
+ * @return A call to #os_dev_isr_return()
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+#define OS_DEV_ISR(function_name) \
+static irqreturn_t function_name(int N1_, void* N2_, struct pt_regs* N3_)
+#else
+#define OS_DEV_ISR(function_name) \
+static irqreturn_t function_name(int N1_, void* N2_)
+#endif
+
+/*!
+ * Declare prototype for an ISR function.
+ *
+ * @param function_name The name of the driver ISR function.
+ */
+#define OS_DEV_ISR_DCL(function_name) \
+OS_DEV_ISR(function_name);
+
+/*!
+ * Generate a function reference to the driver's interrupt service routine
+ * @param function_name Name of the OS_DEV_ISR() function.
+ *
+ * @return A function pointer.
+ */
+#define OS_DEV_ISR_REF(function_name) \
+function_name
+
+/*!
+ * Define a function which will operate as a background task / bottom half.
+ *
+ * Tasklet stuff isn't strictly limited to 'Device drivers', but leave it
+ * this namespace anyway.
+ *
+ * @param function_name The name of this background task function
+ *
+ * @return A call to #os_dev_task_return()
+ */
+#define OS_DEV_TASK(function_name) \
+static void function_name(unsigned long data_)
+
+/*!
+ * Declare prototype for a background task / bottom half function
+ *
+ * @param function_name The name of this background task function
+ */
+#define OS_DEV_TASK_DCL(function_name) \
+OS_DEV_TASK(function_name); \
+DECLARE_TASKLET(function_name ## let, function_name, 0);
+
+/*!
+ * Generate a reference to an #OS_DEV_TASK() function
+ *
+ * @param function_name The name of the task being referenced.
+ */
+#define OS_DEV_TASK_REF(function_name) \
+ (function_name ## let)
+
+ /*! @} *//* drsigs */
+
+/*****************************************************************************
+ * Functions/Macros for returning values from Driver Signature routines
+ *****************************************************************************/
+
+/*!
+ * Return from the #OS_DEV_INIT() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+#define os_dev_init_return(code) \
+ return code
+
+/*!
+ * Return from the #OS_DEV_SHUTDOWN() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+#define os_dev_shutdown_return(code) \
+ return
+
+/*!
+ * Return from the #OS_DEV_ISR() function
+ *
+ * The function should verify that it really was supposed to be called,
+ * and that its device needed attention, in order to properly set the
+ * return code.
+ *
+ * @param code non-zero if interrupt handled, zero otherwise.
+ *
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+#define os_dev_isr_return(code) \
+do { \
+ /* Unused warnings */ \
+ (void)N1_; \
+ (void)N2_; \
+ (void)N3_; \
+ \
+ return IRQ_RETVAL(code); \
+} while (0)
+#else
+#define os_dev_isr_return(code) \
+do { \
+ /* Unused warnings */ \
+ (void)N1_; \
+ (void)N2_; \
+ \
+ return IRQ_RETVAL(code); \
+} while (0)
+#endif
+
+/*!
+ * Return from the #OS_DEV_OPEN() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+#define os_dev_open_return(code) \
+do { \
+ int retcode = code; \
+ \
+ /* get rid of 'unused parameter' warnings */ \
+ (void)inode_p_; \
+ (void)file_p_; \
+ \
+ return retcode; \
+} while (0)
+
+/*!
+ * Return from the #OS_DEV_IOCTL() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+#define os_dev_ioctl_return(code) \
+do { \
+ int retcode = code; \
+ \
+ /* get rid of 'unused parameter' warnings */ \
+ (void)inode_p_; \
+ (void)file_p_; \
+ (void)cmd_; \
+ (void)data_; \
+ \
+ return retcode; \
+} while (0)
+
+/*!
+ * Return from the #OS_DEV_READ() function
+ *
+ * @param code Number of bytes read, or an error code to report failure.
+ *
+ */
+#define os_dev_read_return(code) \
+do { \
+ ssize_t retcode = code; \
+ \
+ /* get rid of 'unused parameter' warnings */ \
+ (void)file_p_; \
+ (void)user_buffer_; \
+ (void)count_bytes_; \
+ (void)file_position_; \
+ \
+ return retcode; \
+} while (0)
+
+/*!
+ * Return from the #OS_DEV_WRITE() function
+ *
+ * @param code Number of bytes written, or an error code to report failure.
+ *
+ */
+#define os_dev_write_return(code) \
+do { \
+ ssize_t retcode = code; \
+ \
+ /* get rid of 'unused parameter' warnings */ \
+ (void)file_p_; \
+ (void)user_buffer_; \
+ (void)count_bytes_; \
+ (void)file_position_; \
+ \
+ return retcode; \
+} while (0)
+
+/*!
+ * Return from the #OS_DEV_CLOSE() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+#define os_dev_close_return(code) \
+do { \
+ ssize_t retcode = code; \
+ \
+ /* get rid of 'unused parameter' warnings */ \
+ (void)inode_p_; \
+ (void)file_p_; \
+ \
+ return retcode; \
+} while (0)
+
+/*!
+ * Start the #OS_DEV_TASK() function
+ *
+ * In some implementations, this could be turned into a label for
+ * the os_dev_task_return() call.
+ *
+ * @return none
+ */
+#define os_dev_task_begin()
+
+/*!
+ * Return from the #OS_DEV_TASK() function
+ *
+ * In some implementations, this could be turned into a sleep followed
+ * by a jump back to the os_dev_task_begin() call.
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+#define os_dev_task_return(code) \
+do { \
+ /* Unused warnings */ \
+ (void)data_; \
+ \
+ return; \
+} while (0)
+
+/*****************************************************************************
+ * Functions/Macros for accessing arguments from Driver Signature routines
+ *****************************************************************************/
+
+/*! @defgroup drsigargs Functions for Getting Arguments in Signature functions
+ *
+ */
+/* @addtogroup @drsigargs */
+/*! @{ */
+/*!
+ * Used in #OS_DEV_OPEN(), #OS_DEV_CLOSE(), #OS_DEV_IOCTL(), #OS_DEV_READ() and
+ * #OS_DEV_WRITE() routines to check whether user is requesting read
+ * (permission)
+ */
+#define os_dev_is_flag_read() \
+ (file_p_->f_mode & FMODE_READ)
+
+/*!
+ * Used in #OS_DEV_OPEN(), #OS_DEV_CLOSE(), #OS_DEV_IOCTL(), #OS_DEV_READ() and
+ * #OS_DEV_WRITE() routines to check whether user is requesting write
+ * (permission)
+ */
+#define os_dev_is_flag_write() \
+ (file_p_->f_mode & FMODE_WRITE)
+
+/*!
+ * Used in #OS_DEV_OPEN(), #OS_DEV_CLOSE(), #OS_DEV_IOCTL(), #OS_DEV_READ() and
+ * #OS_DEV_WRITE() routines to check whether user is requesting non-blocking
+ * I/O.
+ */
+#define os_dev_is_flag_nonblock() \
+ (file_p_->f_flags & (O_NONBLOCK | O_NDELAY))
+
+/*!
+ * Used in #OS_DEV_OPEN() and #OS_DEV_CLOSE() to determine major device being
+ * accessed.
+ */
+#define os_dev_get_major() \
+ (imajor(inode_p_))
+
+/*!
+ * Used in #OS_DEV_OPEN() and #OS_DEV_CLOSE() to determine minor device being
+ * accessed.
+ */
+#define os_dev_get_minor() \
+ (iminor(inode_p_))
+
+/*!
+ * Used in #OS_DEV_IOCTL() to determine which operation the user wants
+ * performed.
+ *
+ * @return Value of the operation.
+ */
+#define os_dev_get_ioctl_op() \
+ (cmd_)
+
+/*!
+ * Used in #OS_DEV_IOCTL() to return the associated argument for the desired
+ * operation.
+ *
+ * @return A value which can be cast to a struct pointer or used as
+ * int/long.
+ */
+#define os_dev_get_ioctl_arg() \
+ (data_)
+
+/*!
+ * Used in OS_DEV_READ() and OS_DEV_WRITE() routines to access the requested
+ * byte count.
+ *
+ * @return (unsigned) a count of bytes
+ */
+#define os_dev_get_count() \
+ ((unsigned)count_bytes_)
+
+/*!
+ * Used in OS_DEV_READ() and OS_DEV_WRITE() routines to return the pointer
+ * byte count.
+ *
+ * @return char* pointer to user buffer
+ */
+#define os_dev_get_user_buffer() \
+ ((void*)user_buffer_)
+
+/*!
+ * Used in OS_DEV_READ(), OS_DEV_WRITE(), and OS_DEV_IOCTL() routines to
+ * get the POSIX flags field for the associated open file).
+ *
+ * @return The flags associated with the file.
+ */
+#define os_dev_get_file_flags() \
+ (file_p_->f_flags)
+
+/*!
+ * Set the driver's private structure associated with this file/open.
+ *
+ * Generally used during #OS_DEV_OPEN(). See #os_dev_get_user_private().
+ *
+ * @param struct_p The driver data structure to associate with this user.
+ */
+#define os_dev_set_user_private(struct_p) \
+ file_p_->private_data = (void*)(struct_p)
+
+/*!
+ * Get the driver's private structure associated with this file.
+ *
+ * May be used during #OS_DEV_OPEN(), #OS_DEV_READ(), #OS_DEV_WRITE(),
+ * #OS_DEV_IOCTL(), and #OS_DEV_CLOSE(). See #os_dev_set_user_private().
+ *
+ * @return The driver data structure to associate with this user.
+ */
+#define os_dev_get_user_private() \
+ ((void*)file_p_->private_data)
+
+/*!
+ * Get the IRQ associated with this call to the #OS_DEV_ISR() function.
+ *
+ * @return The IRQ (integer) interrupt number.
+ */
+#define os_dev_get_irq() \
+ N1_
+
+ /*! @} *//* drsigargs */
+
+/*!
+ * @defgroup cacheops Cache Operations
+ *
+ * These functions are for synchronizing processor cache with RAM.
+ */
+/*! @addtogroup cacheops */
+/*! @{ */
+
+/*!
+ * Flush and invalidate all cache lines.
+ */
+#if 0
+#define os_flush_cache_all() \
+ flush_cache_all()
+#else
+/* Call ARM fn directly, in case L2cache=on3 not set */
+#define os_flush_cache_all() \
+ v6_flush_kern_cache_all_L2()
+
+/*!
+ * ARM-routine to flush all cache. Defined here, because it exists in no
+ * easy-access header file. ARM-11 with L210 cache only!
+ */
+extern void v6_flush_kern_cache_all_L2(void);
+#endif
+
+/*
+ * These macros are using part of the Linux DMA API. They rely on the
+ * map function to do nothing more than the equivalent clean/inv/flush
+ * operation at the time of the mapping, and do nothing at an unmapping
+ * call, which the Sahara driver code will never invoke.
+ */
+
+/*!
+ * Clean a range of addresses from the cache. That is, write updates back
+ * to (RAM, next layer).
+ *
+ * @param start Starting virtual address
+ * @param len Number of bytes to flush
+ *
+ * @return void
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+#define os_cache_clean_range(start,len) \
+ dma_map_single(NULL, (void*)start, len, DMA_TO_DEVICE)
+#else
+#define os_cache_clean_range(start,len) \
+{ \
+ void *s = (void*)start; \
+ void *e = s + len; \
+ dmac_map_area(s, len, DMA_TO_DEVICE); \
+ outer_clean_range(__pa(s), __pa(e)); \
+}
+#endif
+
+/*!
+ * Invalidate a range of addresses in the cache
+ *
+ * @param start Starting virtual address
+ * @param len Number of bytes to flush
+ *
+ * @return void
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+#define os_cache_inv_range(start,len) \
+ dma_map_single(NULL, (void*)start, len, DMA_FROM_DEVICE)
+#else
+#define os_cache_inv_range(start,len) \
+{ \
+ void *s = (void*)start; \
+ void *e = s + len; \
+ dmac_unmap_area(s, len, DMA_FROM_DEVICE); \
+ outer_inv_range(__pa(s), __pa(e)); \
+}
+#endif
+
+/*!
+ * Flush a range of addresses from the cache. That is, perform clean
+ * and invalidate
+ *
+ * @param start Starting virtual address
+ * @param len Number of bytes to flush
+ *
+ * @return void
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+#define os_cache_flush_range(start,len) \
+ dma_map_single(NULL, (void*)start, len, DMA_BIDIRECTIONAL)
+#else
+#define os_cache_flush_range(start,len) \
+{ \
+ void *s = (void*)start; \
+ void *e = s + len; \
+ dmac_flush_range(s, e); \
+ outer_flush_range(__pa(s), __pa(e)); \
+}
+#endif
+
+ /*! @} *//* cacheops */
+
+#endif /* LINUX_PORT_H */
diff --git a/drivers/mxc/security/sahara2/include/platform_abstractions.h b/drivers/mxc/security/sahara2/include/platform_abstractions.h
new file mode 100644
index 000000000000..5fc87ccc5161
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/platform_abstractions.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+/*!
+ * @file platform_abstractions.h
+ */
diff --git a/drivers/mxc/security/sahara2/include/portable_os.h b/drivers/mxc/security/sahara2/include/portable_os.h
new file mode 100644
index 000000000000..445acd5d420c
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/portable_os.h
@@ -0,0 +1,1453 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef PORTABLE_OS_H
+#define PORTABLE_OS_H
+
+/***************************************************************************/
+
+/*
+ * Add support for your target OS by checking appropriate flags and then
+ * including the appropriate file. Don't forget to document the conditions
+ * in the later documentation section at the beginning of the
+ * DOXYGEN_PORTABLE_OS_DOC.
+ */
+
+#if defined(LINUX_KERNEL)
+
+#include "linux_port.h"
+
+#elif defined(PORTABLE_OS)
+
+#include "check_portability.h"
+
+#else
+
+#error Target OS unsupported or unspecified
+
+#endif
+
+
+/***************************************************************************/
+
+/*!
+ * @file portable_os.h
+ *
+ * This file should be included by portable driver code in order to gain access
+ * to the OS-specific header files. It is the only OS-related header file that
+ * the writer of a portable driver should need.
+ *
+ * This file also contains the documentation for the common API.
+ *
+ * Begin reading the documentation for this file at the @ref index "main page".
+ *
+ */
+
+/*!
+ * @if USE_MAINPAGE
+ * @mainpage Generic OS API for STC Drivers
+ * @endif
+ *
+ * @section intro_sec Introduction
+ *
+ * This defines the API / kernel programming environment for portable drivers.
+ *
+ * This API is broken up into several functional areas. It greatly limits the
+ * choices of a device-driver author, but at the same time should allow for
+ * greater portability of the resulting code.
+ *
+ * Each kernel-to-driver function (initialization function, interrupt service
+ * routine, etc.) has a 'portable signature' which must be used, and a specific
+ * function which must be called to generate the return statement. There is
+ * one exception, a background task or "bottom half" routine, which instead has
+ * a specific structure which must be followed. These signatures and function
+ * definitions are found in @ref drsigs.
+ *
+ * None of these kernel-to-driver functions seem to get any arguments passed to
+ * them. Instead, there are @ref drsigargs which allow one of these functions
+ * to get at fairly generic parts of its calling arguments, if there are any.
+ *
+ * Almost every driver will have some need to call the operating system
+ * @ref dkops is the list of services which are available to the driver.
+ *
+ *
+ * @subsection warn_sec Warning
+ *
+ * The specifics of the types, values of the various enumerations
+ * (unless specifically stated, like = 0), etc., are only here for illustrative
+ * purposes. No attempts should be made to make use of any specific knowledge
+ * gleaned from this documentation. These types are only meant to be passed in
+ * and out of the API, and their contents are to be handled only by the
+ * provided OS-specific functions.
+ *
+ * Also, note that the function may be provided as macros in some
+ * implementations, or vice versa.
+ *
+ *
+ * @section dev_sec Writing a Portable Driver
+ *
+ * First off, writing a portable driver means calling no function in an OS
+ * except what is available through this header file.
+ *
+ * Secondly, all OS-called functions in your driver must be invoked and
+ * referenced through the signature routines.
+ *
+ * Thirdly, there might be some rules which you can get away with ignoring or
+ * violating on one OS, yet will cause your code not to be portable to a
+ * different OS.
+ *
+ *
+ * @section limit_sec Limitations
+ *
+ * This API is not expected to handle every type of driver which may be found
+ * in an operating system. For example, it will not be able to handle the
+ * usual design for something like a UART driver, where there are multiple
+ * logical devices to access, because the design usually calls for some sort of
+ * indication to the #OS_DEV_TASK() function or OS_DEV_ISR() to indicate which
+ * channel is to be serviced by that instance of the task/function. This sort
+ * of argument is missing in this API for functions like os_dev_schedule_task() and
+ * os_register_interrupt().
+ *
+ *
+ * @section port_guide Porting Guidelines
+ *
+ * This section is intended for a developer who needs to port the header file
+ * to an operating system which is not yet supported.
+ *
+ * This interface allows for a lot of flexibility when it comes to porting to
+ * an operating systems device driver interface. There are three main areas to
+ * examine: The use of Driver Routine Signatures, the use of Driver Argument
+ * Access functions, the Calls to Kernel Functions, and Data Types.
+ *
+ *
+ * @subsection port_sig Porting Driver Routine Signatures
+ *
+ * The three macros for each function (e.g. #OS_DEV_INIT(), #OS_DEV_INIT_DCL(),
+ * and #OS_DEV_INIT_REF()) allow the flexibility of having a 'wrapper' function
+ * with the OS-required signature, which would then call the user's function
+ * with some different signature.
+ *
+ * The first form would lay down the wrapper function, followed by the
+ * signature for the user function. The second form would lay down just the
+ * signatures for both functions, and the last function would reference the
+ * wrapper function, since that is the interface function called by the OS.
+ *
+ * Note that the driver author has no visibility at all to the signature of the
+ * routines. The author can access arguments only through a limited set of
+ * functions, and must return via another function.
+ *
+ * The Return Functions allow a lot of flexibility in converting the return
+ * value, or not returning a value at all. These will likely be implemented as
+ * macros.
+ *
+ *
+ * @subsection port_arg Porting Driver Argument Access Functions
+ *
+ * The signatures defined by the guide will usually be replaced with macro
+ * definitions.
+ *
+ *
+ * @subsection port_dki Porting Calls to Kernel Functions
+ *
+ * The signatures defined by the guide may be replaced with macro definitions,
+ * if that makes more sense.
+ *
+ * Implementors are free to ignore arguments which are not applicable to their
+ * OS.
+ *
+ * @subsection port_datatypes Porting Data Types
+ *
+ *
+ */
+
+/***************************************************************************
+ * Compile flags
+ **************************************************************************/
+
+/*
+ * This compile flag should only be turned on when running doxygen to generate
+ * the API documentation.
+ */
+#ifdef DOXYGEN_PORTABLE_OS_DOC
+
+/*!
+ * @todo module_init()/module_cleanup() for Linux need to be added to OS
+ * abstractions. Also need EXPORT_SYMBOL() equivalent??
+ *
+ */
+
+/* Drop OS differentation documentation here */
+
+/*!
+ * \#define this flag to build your driver as a Linux driver
+ */
+#define LINUX
+
+/* end OS differentation documentation */
+
+/*!
+ * Symbol to give version number of the implementation file. Earliest
+ * definition is in version 1.1, with value 101 (to mean version 1.1)
+ */
+#define PORTABLE_OS_VERSION 101
+
+/*
+ * NOTICE: The following definitions (the rest of the file) are not meant ever
+ * to be compiled. Instead, they are the documentation for the portable OS
+ * API, to be used by driver writers.
+ *
+ * Each individual OS port will define each of these types, functions, or
+ * macros as appropriate to the target OS. This is why they are under the
+ * DOXYGEN_PORTABLE_OS_DOC flag.
+ */
+
+/***************************************************************************
+ * Type definitions
+ **************************************************************************/
+
+/*!
+ * Type used for registering and deregistering interrupts.
+ *
+ * This is typically an interrupt channel number.
+ */
+typedef int os_interrupt_id_t;
+
+/*!
+ * Type used as handle for a process
+ *
+ * See #os_get_process_handle() and #os_send_signal().
+ */
+typedef int os_process_handle_t;
+
+/*!
+ * Generic return code for functions which need such a thing.
+ *
+ * No knowledge should be assumed of the value of any of these symbols except
+ * that @c OS_ERROR_OK_S is guaranteed to be zero.
+ *
+ * @todo Any other named values? What about (-EAGAIN? -ERESTARTSYS? Are they
+ * too Linux/Unix-specific read()/write() return values) ?
+ */
+typedef enum {
+ OS_ERROR_OK_S = 0, /*!< Success */
+ OS_ERROR_FAIL_S, /*!< Generic driver failure */
+ OS_ERROR_NO_MEMORY_S, /*!< Failure to acquire/use memory */
+ OS_ERROR_BAD_ADDRESS_S, /*!< Bad address */
+ OS_ERROR_BAD_ARG_S /*!< Bad input argument */
+} os_error_code;
+
+/*!
+ * Handle to a lock.
+ */
+typedef int *os_lock_t;
+
+/*!
+ * Context while locking.
+ */
+typedef int os_lock_context_t;
+
+/*!
+ * An object which can be slept on and later used to wake any/all sleepers.
+ */
+typedef int os_sleep_object_t;
+
+/*!
+ * Driver registration handle
+ */
+typedef void *os_driver_reg_t;
+
+/*!
+ * Function signature for an #OS_DEV_INIT() function.
+ *
+ * @return A call to os_dev_init_return() function.
+ */
+typedef void (*os_init_function_t) (void);
+
+/*!
+ * Function signature for an #OS_DEV_SHUTDOWN() function.
+ *
+ * @return A call to os_dev_shutdown_return() function.
+ */
+typedef void (*os_shutdown_function_t) (void);
+
+/*!
+ * Function signature for a user-driver function.
+ *
+ * @return A call to the appropriate os_dev_xxx_return() function.
+ */
+typedef void (*os_user_function_t) (void);
+
+/*!
+ * Function signature for the portable interrupt handler
+ *
+ * While it would be nice to know which interrupt is being serviced, the
+ * Least Common Denominator rule says that no arguments get passed in.
+ *
+ * @return A call to #os_dev_isr_return()
+ */
+typedef void (*os_interrupt_handler_t) (void);
+
+/*!
+ * Function signature for a task function
+ *
+ * Many task function definitions get some sort of generic argument so that the
+ * same function can run across many (queues, channels, ...) as separate task
+ * instances. This has been left out of this API.
+ *
+ * This function must be structured as documented by #OS_DEV_TASK().
+ *
+ */
+typedef void (*os_task_fn_t) (void);
+
+/*!
+ * Function types which can be associated with driver entry points. These are
+ * used in os_driver_add_registration().
+ *
+ * Note that init and shutdown are absent.
+ */
+typedef enum {
+ OS_FN_OPEN, /*!< open() operation handler. */
+ OS_FN_CLOSE, /*!< close() operation handler. */
+ OS_FN_READ, /*!< read() operation handler. */
+ OS_FN_WRITE, /*!< write() operation handler. */
+ OS_FN_IOCTL, /*!< ioctl() operation handler. */
+ OS_FN_MMAP /*!< mmap() operation handler. */
+} os_driver_fn_t;
+
+/***************************************************************************
+ * Driver-to-Kernel Operations
+ **************************************************************************/
+
+/*!
+ * @defgroup dkops Driver-to-Kernel Operations
+ *
+ * These are the operations which drivers should call to get the OS to perform
+ * services.
+ */
+
+/*! @addtogroup dkops */
+/*! @{ */
+
+/*!
+ * Register an interrupt handler.
+ *
+ * @param driver_name The name of the driver
+ * @param interrupt_id The interrupt line to monitor (type
+ * #os_interrupt_id_t)
+ * @param function The function to be called to handle an interrupt
+ *
+ * @return #os_error_code
+ */
+os_error_code os_register_interrupt(char *driver_name,
+ os_interrupt_id_t interrupt_id,
+ os_interrupt_handler_t function);
+
+/*!
+ * Deregister an interrupt handler.
+ *
+ * @param interrupt_id The interrupt line to stop monitoring
+ *
+ * @return #os_error_code
+ */
+os_error_code os_deregister_interrupt(os_interrupt_id_t interrupt_id);
+
+/*!
+ * Initialize driver registration.
+ *
+ * If the driver handles open(), close(), ioctl(), read(), write(), or mmap()
+ * calls, then it needs to register their location with the kernel so that they
+ * get associated with the device.
+ *
+ * @param handle The handle object to be used with this registration. The
+ * object must live (be in memory somewhere) at least until
+ * os_driver_remove_registration() is called.
+ *
+ * @return An os error code.
+ */
+os_error_code os_driver_init_registration(os_driver_reg_t handle);
+
+/*!
+ * Add a function registration to driver registration.
+ *
+ * @param handle The handle used with #os_driver_init_registration().
+ * @param name Which function is being supported.
+ * @param function The result of a call to a @c _REF version of one of the
+ * driver function signature macros
+ * driver function signature macros
+ * @return void
+ */
+void os_driver_add_registration(os_driver_reg_t handle, os_driver_fn_t name,
+ void *function);
+
+/*!
+ * Finalize the driver registration with the kernel.
+ *
+ * Upon return from this call, the driver may begin receiving calls at the
+ * defined entry points.
+ *
+ * @param handle The handle used with #os_driver_init_registration().
+ * @param major The major device number to be associated with the driver.
+ * If this value is zero, a major number may be assigned.
+ * See #os_driver_get_major() to determine final value.
+ * #os_driver_remove_registration().
+ * @param driver_name The driver name. Can be used as part of 'device node'
+ * name on platforms which support such a feature.
+ *
+ * @return An error code
+ */
+os_error_code os_driver_complete_registration(os_driver_reg_t handle,
+ int major, char *driver_name);
+
+/*!
+ * Get driver Major Number from handle after a successful registration.
+ *
+ * @param handle A handle which has completed registration.
+ *
+ * @return The major number (if any) associated with the handle.
+ */
+uint32_t os_driver_get_major(os_driver_reg_t handle);
+
+/*!
+ * Remove the driver's registration with the kernel.
+ *
+ * Upon return from this call, the driver not receive any more calls at the
+ * defined entry points (other than ISR and shutdown).
+ *
+ * @param major The major device number to be associated with the driver.
+ * @param driver_name The driver name
+ *
+ * @return An error code.
+ */
+os_error_code os_driver_remove_registration(int major, char *driver_name);
+
+/*!
+ * Print a message to console / into log file. After the @c msg argument a
+ * number of printf-style arguments may be added. Types should be limited to
+ * printf string, char, octal, decimal, and hexadecimal types. (This excludes
+ * pointers, and floating point).
+ *
+ * @param msg The message to print to console / system log
+ *
+ * @return (void)
+ */
+void os_printk(char *msg, ...);
+
+/*!
+ * Allocate some kernel memory
+ *
+ * @param amount Number of 8-bit bytes to allocate
+ * @param flags Some indication of purpose of memory (needs definition)
+ *
+ * @return Pointer to allocated memory, or NULL if failed.
+ */
+void *os_alloc_memory(unsigned amount, int flags);
+
+/*!
+ * Free some kernel memory
+ *
+ * @param location The beginning of the region to be freed.
+ *
+ * Do some OSes have separate free() functions which should be
+ * distinguished by passing in @c flags here, too? Don't some also require the
+ * size of the buffer being freed? Perhaps separate routines for each
+ * alloc/free pair (DMAable, etc.)?
+ */
+void os_free_memory(void *location);
+
+/*!
+ * Allocate cache-coherent memory
+ *
+ * @param amount Number of bytes to allocate
+ * @param[out] dma_addrp Location to store physical address of allocated
+ * memory.
+ * @param flags Some indication of purpose of memory (needs
+ * definition).
+ *
+ * @return (virtual space) pointer to allocated memory, or NULL if failed.
+ *
+ */
+void *os_alloc_coherent(unsigned amount, uint32_t * dma_addrp, int flags);
+
+/*!
+ * Free cache-coherent memory
+ *
+ * @param size Number of bytes which were allocated.
+ * @param[out] virt_addr Virtual(kernel) address of memory.to be freed, as
+ * returned by #os_alloc_coherent().
+ * @param[out] dma_addr Physical address of memory.to be freed, as returned
+ * by #os_alloc_coherent().
+ *
+ * @return void
+ *
+ */
+void os_free_coherent(unsigned size, void *virt_addr, uint32_t dma_addr);
+
+/*!
+ * Map an I/O space into kernel memory space
+ *
+ * @param start The starting address of the (physical / io space) region
+ * @param range_bytes The number of bytes to map
+ *
+ * @return A pointer to the mapped area, or NULL on failure
+ */
+void *os_map_device(uint32_t start, unsigned range_bytes);
+
+/*!
+ * Unmap an I/O space from kernel memory space
+ *
+ * @param start The starting address of the (virtual) region
+ * @param range_bytes The number of bytes to unmap
+ *
+ * @return None
+ */
+void os_unmap_device(void *start, unsigned range_bytes);
+
+/*!
+ * Copy data from Kernel space to User space
+ *
+ * @param to The target location in user memory
+ * @param from The source location in kernel memory
+ * @param size The number of bytes to be copied
+ *
+ * @return #os_error_code
+ */
+os_error_code os_copy_to_user(void *to, void *from, unsigned size);
+
+/*!
+ * Copy data from User space to Kernel space
+ *
+ * @param to The target location in kernel memory
+ * @param from The source location in user memory
+ * @param size The number of bytes to be copied
+ *
+ * @return #os_error_code
+ */
+os_error_code os_copy_from_user(void *to, void *from, unsigned size);
+
+/*!
+ * Read an 8-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @return The value in the register
+ */
+uint8_t os_read8(uint8_t * register_address);
+
+/*!
+ * Write an 8-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @param value The value to write into the register
+ */
+void os_write8(uint8_t * register_address, uint8_t value);
+
+/*!
+ * Read a 16-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @return The value in the register
+ */
+uint16_t os_read16(uint16_t * register_address);
+
+/*!
+ * Write a 16-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @param value The value to write into the register
+ */
+void os_write16(uint16_t * register_address, uint16_t value);
+
+/*!
+ * Read a 32-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @return The value in the register
+ */
+uint32_t os_read32(uint32_t * register_address);
+
+/*!
+ * Write a 32-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @param value The value to write into the register
+ */
+void os_write32(uint32_t * register_address, uint32_t value);
+
+/*!
+ * Read a 64-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @return The value in the register
+ */
+uint64_t os_read64(uint64_t * register_address);
+
+/*!
+ * Write a 64-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @param value The value to write into the register
+ */
+void os_write64(uint64_t * register_address, uint64_t value);
+
+/*!
+ * Prepare a task to execute the given function. This should only be done once
+ * per task, during the driver's initialization routine.
+ *
+ * @param task_fn Name of the OS_DEV_TASK() function to be created.
+ *
+ * @return an OS ERROR code.
+ */
+os_error os_create_task(os_task_fn_t * task_fn);
+
+/*!
+ * Run the task associated with an #OS_DEV_TASK() function
+ *
+ * The task will begin execution sometime after or during this call.
+ *
+ * @param task_fn Name of the OS_DEV_TASK() function to be scheduled.
+ *
+ * @return void
+ */
+void os_dev_schedule_task(os_task_fn_t * task_fn);
+
+/*!
+ * Make sure that task is no longer running and will no longer run.
+ *
+ * This function will not return until both are true. This is useful when
+ * shutting down a driver.
+ *
+ * @param task_fn Name of the OS_DEV_TASK() funciton to be stopped.
+ *
+ */
+void os_stop_task(os_task_fn_t * task_fn);
+
+/*!
+ * Delay some number of microseconds
+ *
+ * Note that this is a busy-loop, not a suspension of the task/process.
+ *
+ * @param msecs The number of microseconds to delay
+ *
+ * @return void
+ */
+void os_mdelay(unsigned long msecs);
+
+/*!
+ * Calculate virtual address from physical address
+ *
+ * @param pa Physical address
+ *
+ * @return virtual address
+ *
+ * @note this assumes that addresses are 32 bits wide
+ */
+void *os_va(uint32_t pa);
+
+/*!
+ * Calculate physical address from virtual address
+ *
+ *
+ * @param va Virtual address
+ *
+ * @return physical address
+ *
+ * @note this assumes that addresses are 32 bits wide
+ */
+uint32_t os_pa(void *va);
+
+/*!
+ * Allocate and initialize a lock, returning a lock handle.
+ *
+ * The lock state will be initialized to 'unlocked'.
+ *
+ * @return A lock handle, or NULL if an error occurred.
+ */
+os_lock_t os_lock_alloc_init(void);
+
+/*!
+ * Acquire a lock.
+ *
+ * This function should only be called from an interrupt service routine.
+ *
+ * @param lock_handle A handle to the lock to acquire.
+ *
+ * @return void
+ */
+void os_lock(os_lock_t lock_handle);
+
+/*!
+ * Unlock a lock. Lock must have been acquired by #os_lock().
+ *
+ * @param lock_handle A handle to the lock to unlock.
+ *
+ * @return void
+ */
+void os_unlock(os_lock_t lock_handle);
+
+/*!
+ * Acquire a lock in non-ISR context
+ *
+ * This function will spin until the lock is available.
+ *
+ * @param lock_handle A handle of the lock to acquire.
+ * @param context Place to save the before-lock context
+ *
+ * @return void
+ */
+void os_lock_save_context(os_lock_t lock_handle, os_lock_context_t context);
+
+/*!
+ * Release a lock in non-ISR context
+ *
+ * @param lock_handle A handle of the lock to release.
+ * @param context Place where before-lock context was saved.
+ *
+ * @return void
+ */
+void os_unlock_restore_context(os_lock_t lock_handle,
+ os_lock_context_t context);
+
+/*!
+ * Deallocate a lock handle.
+ *
+ * @param lock_handle An #os_lock_t that has been allocated.
+ *
+ * @return void
+ */
+void os_lock_deallocate(os_lock_t lock_handle);
+
+/*!
+ * Determine process handle
+ *
+ * The process handle of the current user is returned.
+ *
+ * @return A handle on the current process.
+ */
+os_process_handle_t os_get_process_handle();
+
+/*!
+ * Send a signal to a process
+ *
+ * @param proc A handle to the target process.
+ * @param sig The POSIX signal to send to that process.
+ */
+void os_send_signal(os_process_handle_t proc, int sig);
+
+/*!
+ * Get some random bytes
+ *
+ * @param buf The location to store the random data.
+ * @param count The number of bytes to store.
+ *
+ * @return void
+ */
+void os_get_random_bytes(void *buf, unsigned count);
+
+/*!
+ * Go to sleep on an object.
+ *
+ * Example: code = os_sleep(my_queue, available_count == 0, 0);
+ *
+ * @param object The object on which to sleep
+ * @param condition An expression to check for sleep completion. Must be
+ * coded so that it can be referenced more than once inside
+ * macro, i.e., no ++ or other modifying expressions.
+ * @param atomic Non-zero if sleep must not return until condition.
+ *
+ * @return error code -- OK or sleep interrupted??
+ */
+os_error_code os_sleep(os_sleep_object_t object, unsigned condition,
+ unsigned atomic);
+
+/*!
+ * Wake up whatever is sleeping on sleep object
+ *
+ * @param object The object on which things might be sleeping
+ *
+ * @return none
+ */
+void os_wake_sleepers(os_sleep_object_t object);
+
+ /*! @} *//* dkops */
+
+/*****************************************************************************
+ * Function-signature-generating macros
+ *****************************************************************************/
+
+/*!
+ * @defgroup drsigs Driver Function Signatures
+ *
+ * These macros will define the entry point signatures for interrupt handlers;
+ * driver initialization and shutdown; device open/close; etc. They are to be
+ * used whenever the Kernel will call into the Driver. They are not
+ * appropriate for driver calls to other routines in the driver.
+ *
+ * There are three versions of each macro for a given Driver Entry Point. The
+ * first version is used to define a function and its implementation in the
+ * driver.c file, e.g. #OS_DEV_INIT().
+ *
+ * The second form is used whenever a forward declaration (prototype) is
+ * needed. It has the letters @c _DCL appended to the name of the definition
+ * function. These are not otherwise mentioned in this documenation.
+ *
+ * There is a third form used when a reference to a function is required, for
+ * instance when passing the routine as a pointer to a function. It has the
+ * letters @c _REF appended to the name of the definition function
+ * (e.g. DEV_IOCTL_REF).
+ *
+ * Note that these two extra forms are required because of the possibility of
+ * having an invisible 'wrapper function' created by the os-specific header
+ * file which would need to be invoked by the operating system, and which in
+ * turn would invoke the generic function.
+ *
+ * Example:
+ *
+ * (in a header file)
+ * @code
+ * OS_DEV_INIT_DCL(widget_init);
+ * OS_DEV_ISR_DCL(widget_isr);
+ * @endcode
+ *
+ * (in an implementation file)
+ * @code
+ * OS_DEV_INIT(widget, widget_init)
+ * {
+ *
+ * os_register_interrupt("widget", WIDGET_IRQ, OS_DEV_ISR_REF(widget_isr));
+ *
+ * os_dev_init_return(OS_RETURN_NO_ERROR_S);
+ * }
+ *
+ * OS_DEV_ISR(widget_isr)
+ * {
+ * os_dev_isr_return(TRUE);
+ * }
+ * @endcode
+ */
+
+/*! @addtogroup drsigs */
+/*! @{ */
+
+/*!
+ * Define a function which will handle device initialization
+ *
+ * This is tne driver initialization routine. This is normally where the
+ * part would be initialized; queues, locks, interrupts handlers defined;
+ * long-term dynamic memory allocated for driver use; etc.
+ *
+ * @param function_name The name of the portable initialization function.
+ *
+ * @return A call to #os_dev_init_return()
+ *
+ */
+#define OS_DEV_INIT(function_name)
+
+/*!
+ * Define a function which will handle device shutdown
+ *
+ * This is the reverse of the #OS_DEV_INIT() routine.
+ *
+ * @param function_name The name of the portable driver shutdown routine.
+ *
+ * @return A call to #os_dev_shutdown_return()
+ */
+#define OS_DEV_SHUTDOWN(function_name)
+
+/*!
+ * Define a function which will open the device for a user.
+ *
+ * @param function_name The name of the driver open() function
+ *
+ * @return A call to #os_dev_open_return()
+ */
+#define OS_DEV_OPEN(function_name)
+
+/*!
+ * Define a function which will handle a user's ioctl() request
+ *
+ * @param function_name The name of the driver ioctl() function
+ *
+ * @return A call to #os_dev_ioctl_return()
+ */
+#define OS_DEV_IOCTL(function_name)
+
+/*!
+ * Define a function which will handle a user's read() request
+ *
+ * @param function_name The name of the driver read() function
+ *
+ * @return A call to #os_dev_read_return()
+ */
+#define OS_DEV_READ(function_name)
+
+/*!
+ * Define a function which will handle a user's write() request
+ *
+ * @param function_name The name of the driver write() function
+ *
+ * @return A call to #os_dev_write_return()
+ */
+#define OS_DEV_WRITE(function_name)
+
+/*!
+ * Define a function which will handle a user's mmap() request
+ *
+ * The mmap() function requests the driver to map some memory into user space.
+ *
+ * @todo Determine what support functions are needed for mmap() handling.
+ *
+ * @param function_name The name of the driver mmap() function
+ *
+ * @return A call to #os_dev_mmap_return()
+ */
+#define OS_DEV_MMAP(function_name)
+
+/*!
+ * Define a function which will close the device - opposite of OS_DEV_OPEN()
+ *
+ * @param function_name The name of the driver close() function
+ *
+ * @return A call to #os_dev_close_return()
+ */
+#define OS_DEV_CLOSE(function_name)
+
+/*!
+ * Define a function which will handle an interrupt
+ *
+ * No arguments are available to the generic function. It must not invoke any
+ * OS functions which are illegal in a ISR. It gets no parameters, and must
+ * have a call to #os_dev_isr_return() instead of any/all return statements.
+ *
+ * Example:
+ * @code
+ * OS_DEV_ISR(widget, widget_isr, WIDGET_IRQ_NUMBER)
+ * {
+ * os_dev_isr_return(1);
+ * }
+ * @endcode
+ *
+ * @param function_name The name of the driver ISR function
+ *
+ * @return A call to #os_dev_isr_return()
+ */
+#define OS_DEV_ISR(function_name)
+
+/*!
+ * Define a function which will operate as a background task / bottom half.
+ *
+ * The function implementation must be structured in the following manner:
+ * @code
+ * OS_DEV_TASK(widget_task)
+ * {
+ * OS_DEV_TASK_SETUP(widget_task);
+ *
+ * while OS_DEV_TASK_CONDITION(widget_task) }
+ *
+ * };
+ * }
+ * @endcode
+ *
+ * @todo In some systems the OS_DEV_TASK_CONDITION() will be an action which
+ * will cause the task to sleep on some event triggered by os_run_task(). In
+ * others, the macro will reference a variable laid down by
+ * OS_DEV_TASK_SETUP() to make sure that the loop is only performed once.
+ *
+ * @param function_name The name of this background task function
+ */
+#define OS_DEV_TASK(function_name)
+
+ /*! @} *//* drsigs */
+
+/*! @defgroup dclsigs Routines to declare Driver Signature routines
+ *
+ * These macros drop prototypes suitable for forward-declaration of
+ * @ref drsigs "function signatures".
+ */
+
+/*! @addtogroup dclsigs */
+/*! @{ */
+
+/*!
+ * Declare prototype for the device initialization function
+ *
+ * @param function_name The name of the portable initialization function.
+ */
+#define OS_DEV_INIT_DCL(function_name)
+
+/*!
+ * Declare prototype for the device shutdown function
+ *
+ * @param function_name The name of the portable driver shutdown routine.
+ *
+ * @return A call to #os_dev_shutdown_return()
+ */
+#define OS_DEV_SHUTDOWN_DCL(function_name)
+
+/*!
+ * Declare prototype for the open() function.
+ *
+ * @param function_name The name of the driver open() function
+ *
+ * @return A call to #os_dev_open_return()
+ */
+#define OS_DEV_OPEN_DCL(function_name)
+
+/*!
+ * Declare prototype for the user's ioctl() request function
+ *
+ * @param function_name The name of the driver ioctl() function
+ *
+ * @return A call to #os_dev_ioctl_return()
+ */
+#define OS_DEV_IOCTL_DCL(function_name)
+
+/*!
+ * Declare prototype for the function a user's read() request
+ *
+ * @param function_name The name of the driver read() function
+ */
+#define OS_DEV_READ_DCL(function_name)
+
+/*!
+ * Declare prototype for the user's write() request function
+ *
+ * @param function_name The name of the driver write() function
+ */
+#define OS_DEV_WRITE_DCL(function_name)
+
+/*!
+ * Declare prototype for the user's mmap() request function
+ *
+ * @param function_name The name of the driver mmap() function
+ */
+#define OS_DEV_MMAP_DCL(function_name)
+
+/*!
+ * Declare prototype for the close function
+ *
+ * @param function_name The name of the driver close() function
+ *
+ * @return A call to #os_dev_close_return()
+ */
+#define OS_DEV_CLOSE_DCL(function_name)
+
+/*!
+ * Declare prototype for the interrupt handling function
+ *
+ * @param function_name The name of the driver ISR function
+ */
+#define OS_DEV_ISR_DCL(function_name)
+
+/*!
+ * Declare prototype for a background task / bottom half function
+ *
+ * @param function_name The name of this background task function
+ */
+#define OS_DEV_TASK_DCL(function_name)
+
+ /*! @} *//* dclsigs */
+
+/*****************************************************************************
+ * Functions for Returning Values from Driver Signature routines
+ *****************************************************************************/
+
+/*!
+ * @defgroup retfns Functions to Return Values from Driver Signature routines
+ */
+
+/*! @addtogroup retfns */
+/*! @{ */
+
+/*!
+ * Return from the #OS_DEV_INIT() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+void os_dev_init_return(os_error_code code);
+
+/*!
+ * Return from the #OS_DEV_SHUTDOWN() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+void os_dev_shutdown_return(os_error_code code);
+
+/*!
+ * Return from the #OS_DEV_ISR() function
+ *
+ * The function should verify that it really was supposed to be called,
+ * and that its device needed attention, in order to properly set the
+ * return code.
+ *
+ * @param code non-zero if interrupt handled, zero otherwise.
+ *
+ */
+void os_dev_isr_return(int code);
+
+/*!
+ * Return from the #OS_DEV_OPEN() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+void os_dev_open_return(os_error_code code);
+
+/*!
+ * Return from the #OS_DEV_IOCTL() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+void os_dev_ioctl_return(os_error_code code);
+
+/*!
+ * Return from the #OS_DEV_READ() function
+ *
+ * @param code Number of bytes read, or an error code to report failure.
+ *
+ */
+void os_dev_read_return(os_error_code code);
+
+/*!
+ * Return from the #OS_DEV_WRITE() function
+ *
+ * @param code Number of bytes written, or an error code to report failure.
+ *
+ */
+void os_dev_write_return(os_error_code code);
+
+/*!
+ * Return from the #OS_DEV_MMAP() function
+ *
+ * @param code Number of bytes written, or an error code to report failure.
+ *
+ */
+void os_dev_mmap_return(os_error_code code);
+
+/*!
+ * Return from the #OS_DEV_CLOSE() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+void os_dev_close_return(os_error_code code);
+
+/*!
+ * Start the #OS_DEV_TASK() function
+ *
+ * In some implementations, this could be turned into a label for
+ * the os_dev_task_return() call.
+ *
+ * For a more portable interface, should this take the sleep object as an
+ * argument???
+ *
+ * @return none
+ */
+void os_dev_task_begin(void);
+
+/*!
+ * Return from the #OS_DEV_TASK() function
+ *
+ * In some implementations, this could be turned into a sleep followed
+ * by a jump back to the os_dev_task_begin() call.
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+void os_dev_task_return(os_error_code code);
+
+ /*! @} *//* retfns */
+
+/*****************************************************************************
+ * Functions/Macros for accessing arguments from Driver Signature routines
+ *****************************************************************************/
+
+/*! @defgroup drsigargs Functions for Getting Arguments in Signature functions
+ *
+ */
+/* @addtogroup @drsigargs */
+/*! @{ */
+
+/*!
+ * Check whether user is requesting read (permission) on the file/device.
+ * Usable in #OS_DEV_OPEN(), #OS_DEV_CLOSE(), #OS_DEV_IOCTL(), #OS_DEV_READ()
+ * and #OS_DEV_WRITE() routines.
+ */
+int os_dev_is_flag_read(void);
+
+/*!
+ * Check whether user is requesting write (permission) on the file/device.
+ * Usable in #OS_DEV_OPEN(), #OS_DEV_CLOSE(), #OS_DEV_IOCTL(), #OS_DEV_READ()
+ * and #OS_DEV_WRITE() routines.
+ */
+int os_dev_is_flag_write(void);
+
+/*!
+ * Check whether user is requesting non-blocking I/O. Usable in
+ * #OS_DEV_OPEN(), #OS_DEV_CLOSE(), #OS_DEV_IOCTL(), #OS_DEV_READ() and
+ * #OS_DEV_WRITE() routines.
+ *
+ * @todo Specify required behavior when nonblock is requested and (sufficient?)
+ * data are not available to fulfill the request.
+ *
+ */
+int os_dev_is_flag_nonblock(void);
+
+/*!
+ * Determine which major device is being accessed. Usable in #OS_DEV_OPEN()
+ * and #OS_DEV_CLOSE().
+ */
+int os_dev_get_major(void);
+
+/*!
+ * Determine which minor device is being accessed. Usable in #OS_DEV_OPEN()
+ * and #OS_DEV_CLOSE().
+ */
+int os_dev_get_minor(void);
+
+/*!
+ * Determine which operation the user wants performed. Usable in
+ * #OS_DEV_IOCTL().
+ *
+ * @return Value of the operation.
+ *
+ * @todo Define some generic way to define the individual operations.
+ */
+unsigned os_dev_get_ioctl_op(void);
+
+/*!
+ * Retrieve the associated argument for the desired operation. Usable in
+ * #OS_DEV_IOCTL().
+ *
+ * @return A value which can be cast to a struct pointer or used as
+ * int/long.
+ */
+os_dev_ioctl_arg_t os_dev_get_ioctl_arg(void);
+
+/*!
+ * Determine the requested byte count. This should be the size of buffer at
+ * #os_dev_get_user_buffer(). Usable in OS_DEV_READ() and OS_DEV_WRITE()
+ * routines.
+ *
+ * @return A count of bytes
+ */
+unsigned os_dev_get_count(void);
+
+/*!
+ * Get the pointer to the user's data buffer. Usable in OS_DEV_READ(),
+ * OS_DEV_WRITE(), and OS_DEV_MMAP() routines.
+ *
+ * @return Pointer to user buffer (in user space). See #os_copy_to_user()
+ * and #os_copy_from_user().
+ */
+void *os_dev_get_user_buffer(void);
+
+/*!
+ * Get the POSIX flags field for the associated open file. Usable in
+ * OS_DEV_READ(), OS_DEV_WRITE(), and OS_DEV_IOCTL() routines.
+ *
+ * @return The flags associated with the file.
+ */
+unsigned os_dev_get_file_flags(void);
+
+/*!
+ * Set the driver's private structure associated with this file/open.
+ *
+ * Generally used during #OS_DEV_OPEN(). May also be used during
+ * #OS_DEV_READ(), #OS_DEV_WRITE(), #OS_DEV_IOCTL(), #OS_DEV_MMAP(), and
+ * #OS_DEV_CLOSE(). See also #os_dev_get_user_private().
+ *
+ * @param struct_p The driver data structure to associate with this user.
+ */
+void os_dev_set_user_private(void *struct_p);
+
+/*!
+ * Get the driver's private structure associated with this file.
+ *
+ * May be used during #OS_DEV_OPEN(), #OS_DEV_READ(), #OS_DEV_WRITE(),
+ * #OS_DEV_IOCTL(), #OS_DEV_MMAP(), and #OS_DEV_CLOSE(). See
+ * also #os_dev_set_user_private().
+ *
+ * @return The driver data structure to associate with this user.
+ */
+void *os_dev_get_user_private(void);
+
+/*!
+ * Get the IRQ associated with this call to the #OS_DEV_ISR() function.
+ *
+ * @return The IRQ (integer) interrupt number.
+ */
+int os_dev_get_irq(void);
+
+ /*! @} *//* drsigargs */
+
+/*****************************************************************************
+ * Functions for Generating References to Driver Routines
+ *****************************************************************************/
+
+/*!
+ * @defgroup drref Functions for Generating References to Driver Routines
+ *
+ * These functions will most likely be implemented as macros. They are a
+ * necessary part of the portable API to guarantee portability. The @c symbol
+ * type in here is the same symbol passed to the associated
+ * signature-generating macro.
+ *
+ * These macros must be used whenever referring to a
+ * @ref drsigs "driver signature function", for instance when storing or
+ * passing a pointer to the function.
+ */
+
+/*! @addtogroup drref */
+/*! @{ */
+
+/*!
+ * Generate a reference to an #OS_DEV_INIT() function
+ *
+ * @param function_name The name of the init function being referenced.
+ *
+ * @return A reference to the function
+ */
+os_init_function_t OS_DEV_INIT_REF(symbol function_name);
+
+/*!
+ * Generate a reference to an #OS_DEV_SHUTDOWN() function
+ *
+ * @param function_name The name of the shutdown function being referenced.
+ *
+ * @return A reference to the function
+ */
+os_shutdown_function_t OS_DEV_SHUTDOWN_REF(symbol function_name);
+
+/*!
+ * Generate a reference to an #OS_DEV_OPEN() function
+ *
+ * @param function_name The name of the open function being referenced.
+ *
+ * @return A reference to the function
+ */
+os_user_function_t OS_DEV_OPEN_REF(symbol function_name);
+
+/*!
+ * Generate a reference to an #OS_DEV_CLOSE() function
+ *
+ * @param function_name The name of the close function being referenced.
+ *
+ * @return A reference to the function
+ */
+os_user_function_t OS_DEV_CLOSE_REF(symbol function_name);
+
+/*!
+ * Generate a reference to an #OS_DEV_READ() function
+ *
+ * @param function_name The name of the read function being referenced.
+ *
+ * @return A reference to the function
+ */
+os_user_function_t OS_DEV_READ_REF(symbol function_name);
+
+/*!
+ * Generate a reference to an #OS_DEV_WRITE() function
+ *
+ * @param function_name The name of the write function being referenced.
+ *
+ * @return A reference to the function
+ */
+os_user_function_t OS_DEV_WRITE_REF(symbol function_name);
+
+/*!
+ * Generate a reference to an #OS_DEV_IOCTL() function
+ *
+ * @param function_name The name of the ioctl function being referenced.
+ *
+ * @return A reference to the function
+ */
+os_user_function_t OS_DEV_IOCTL_REF(symbol function_name);
+
+/*!
+ * Generate a reference to an #OS_DEV_MMAP() function
+ *
+ * @param function_name The name of the mmap function being referenced.
+ *
+ * @return A reference to the function
+ */
+os_user_function_t OS_DEV_MMAP_REF(symbol function_name);
+
+/*!
+ * Generate a reference to an #OS_DEV_ISR() function
+ *
+ * @param function_name The name of the isr being referenced.
+ *
+ * @return a reference to the function
+ */
+os_interrupt_handler_t OS_DEV_ISR_REF(symbol function_name);
+
+ /*! @} *//* drref */
+
+/*!
+ * Flush and invalidate all cache lines.
+ */
+void os_flush_cache_all(void);
+
+/*!
+ * Flush a range of addresses from the cache
+ *
+ * @param start Starting virtual address
+ * @param len Number of bytes to flush
+ */
+void os_cache_flush_range(void *start, uint32_t len);
+
+/*!
+ * Invalidate a range of addresses in the cache
+ *
+ * @param start Starting virtual address
+ * @param len Number of bytes to flush
+ */
+void os_cache_inv_range(void *start, uint32_t len);
+
+/*!
+ * Clean a range of addresses from the cache
+ *
+ * @param start Starting virtual address
+ * @param len Number of bytes to flush
+ */
+void os_cache_clean_range(void *start, uint32_t len);
+
+/*!
+ * @example widget.h
+ */
+
+/*!
+ * @example widget.c
+ */
+
+/*!
+ * @example rng_driver.h
+ */
+
+/*!
+ * @example rng_driver.c
+ */
+
+/*!
+ * @example shw_driver.h
+ */
+
+/*!
+ * @example shw_driver.c
+ */
+
+#endif /* DOXYGEN_PORTABLE_OS_DOC */
+
+#endif /* PORTABLE_OS_H */
diff --git a/drivers/mxc/security/sahara2/include/sah_driver_common.h b/drivers/mxc/security/sahara2/include/sah_driver_common.h
new file mode 100644
index 000000000000..bcdc053bf0c3
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sah_driver_common.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+* @file sah_driver_common.h
+*
+* @brief Provides types and defined values for use in the Driver Interface.
+*
+*/
+
+#ifndef SAH_DRIVER_COMMON_H
+#define SAH_DRIVER_COMMON_H
+
+#include "fsl_platform.h"
+#include <sahara.h>
+#include <adaptor.h>
+
+/** This specifies the permissions for the device file. It is equivalent to
+ * chmod 666.
+ */
+#define SAHARA_DEVICE_MODE S_IFCHR | S_IRUGO | S_IWUGO
+
+/**
+* The status of entries in the Queue.
+*
+******************************************************************************/
+typedef enum
+{
+ /** This state indicates that the entry is in the queue and awaits
+ * execution on SAHARA. */
+ SAH_STATE_PENDING,
+ /** This state indicates that the entry has been written to the SAHARA
+ * DAR. */
+ SAH_STATE_ON_SAHARA,
+ /** This state indicates that the entry is off of SAHARA, and is awaiting
+ post-processing. */
+ SAH_STATE_OFF_SAHARA,
+ /** This state indicates that the entry is successfully executed on SAHARA,
+ and it is finished with post-processing. */
+ SAH_STATE_COMPLETE,
+ /** This state indicates that the entry caused an error or fault on SAHARA,
+ * and it is finished with post-processing. */
+ SAH_STATE_FAILED,
+ /** This state indicates that the entry was reset via the Reset IO
+ * Control, and it is finished with post-processing. */
+ SAH_STATE_RESET,
+ /** This state indicates that the entry was signalled from user-space and
+ * either in the DAR, IDAR or has finished executing pending Bottom Half
+ * processing. */
+ SAH_STATE_IGNORE,
+ /** This state indicates that the entry was signalled from user-space and
+ * has been processed by the bottom half. */
+ SAH_STATE_IGNORED
+} sah_Queue_Status;
+
+/* any of these conditions being true indicates the descriptor's processing
+ * is complete */
+#define SAH_DESC_PROCESSED(status) \
+ (((status) == SAH_STATE_COMPLETE) || \
+ ((status) == SAH_STATE_FAILED ) || \
+ ((status) == SAH_STATE_RESET ))
+
+extern os_lock_t desc_queue_lock;
+
+extern uint32_t dar_count;
+extern uint32_t interrupt_count;
+extern uint32_t done1done2_count;
+extern uint32_t done1busy2_count;
+extern uint32_t done1_count;
+
+#ifdef FSL_HAVE_SCC2
+extern void *lookup_user_partition(fsl_shw_uco_t * user_ctx,
+ uint32_t user_base);
+#endif
+
+int sah_get_results_pointers(fsl_shw_uco_t* user_ctx, uint32_t arg);
+fsl_shw_return_t sah_get_results_from_pool(volatile fsl_shw_uco_t* user_ctx,
+ sah_results *arg);
+fsl_shw_return_t sah_handle_registration(fsl_shw_uco_t *user_cts);
+fsl_shw_return_t sah_handle_deregistration(fsl_shw_uco_t *user_cts);
+
+int sah_Queue_Manager_Count_Entries(int ignore_state, sah_Queue_Status state);
+unsigned long sah_Handle_Poll(sah_Head_Desc *entry);
+
+#ifdef DIAG_DRV_IF
+/******************************************************************************
+* Descriptor and Link dumping functions.
+******************************************************************************/
+void sah_Dump_Chain(const sah_Desc *chain, dma_addr_t addr);
+#endif /* DIAG_DRV_IF */
+
+#endif /* SAH_DRIVER_COMMON_H */
diff --git a/drivers/mxc/security/sahara2/include/sah_hardware_interface.h b/drivers/mxc/security/sahara2/include/sah_hardware_interface.h
new file mode 100644
index 000000000000..f507a835a504
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sah_hardware_interface.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+* @file sah_hardware_interface.h
+*
+* @brief Provides an interface to the SAHARA hardware registers.
+*
+*/
+
+#ifndef SAH_HARDWARE_INTERFACE_H
+#define SAH_HARDWARE_INTERFACE_H
+
+#include <sah_driver_common.h>
+#include <sah_status_manager.h>
+
+/* These values can be used with sah_HW_Write_Control(). */
+#ifdef SAHARA1
+/** Define platform as Little-Endian */
+#define CTRL_LITTLE_END 0x00000002
+/** Bit to cause endian change in transfers */
+#define CTRL_INT_EN 0x00000004
+/** Set High Assurance mode */
+#define CTRL_HA 0x00000008
+#else
+/** Bit to cause byte swapping in (data?) transfers */
+#define CTRL_BYTE_SWAP 0x00000001
+/** Bit to cause halfword swapping in (data?) transfers */
+#define CTRL_HALFWORD_SWAP 0x00000002
+/** Bit to cause endian change in (data?) transfers */
+#define CTRL_INT_EN 0x00000010
+/** Set High Assurance mode */
+#define CTRL_HA 0x00000020
+/** Disable High Assurance */
+#define CTRL_HA_DISABLE 0x00000040
+/** Reseed the RNG CHA */
+#define CTRL_RNG_RESEED 0x00000080
+#endif
+
+
+/* These values can be used with sah_HW_Write_Command(). */
+/** Reset the Sahara */
+#define CMD_RESET 0x00000001
+/** Set Sahara into Batch mode. */
+#define CMD_BATCH 0x00010000
+/** Clear the Sahara interrupt */
+#define CMD_CLR_INT_BIT 0x00000100
+/** Clear the Sahara error */
+#define CMD_CLR_ERROR_BIT 0x00000200
+
+
+/** error status register contains error */
+#define STATUS_ERROR 0x00000010
+
+/** Op status register contains op status */
+#define OP_STATUS 0x00000020
+
+
+/* High Level functions */
+int sah_HW_Reset(void);
+fsl_shw_return_t sah_HW_Set_HA(void);
+sah_Execute_Status sah_Wait_On_Sahara(void);
+
+/* Low Level functions */
+uint32_t sah_HW_Read_Version(void);
+uint32_t sah_HW_Read_Control(void);
+uint32_t sah_HW_Read_Status(void);
+uint32_t sah_HW_Read_Error_Status(void);
+uint32_t sah_HW_Read_Op_Status(void);
+uint32_t sah_HW_Read_DAR(void);
+uint32_t sah_HW_Read_CDAR(void);
+uint32_t sah_HW_Read_IDAR(void);
+uint32_t sah_HW_Read_Fault_Address(void);
+uint32_t sah_HW_Read_MM_Status(void);
+uint32_t sah_HW_Read_Config(void);
+void sah_HW_Write_Command(uint32_t command);
+void sah_HW_Write_Control(uint32_t control);
+void sah_HW_Write_DAR(uint32_t pointer);
+void sah_HW_Write_Config(uint32_t configuration);
+
+#if defined DIAG_DRV_IF || defined(DO_DBG)
+
+void sah_Dump_Words(const char *prefix, const unsigned *data, dma_addr_t addr,
+ unsigned length);
+#endif
+
+#endif /* SAH_HARDWARE_INTERFACE_H */
+
+/* End of sah_hardware_interface.c */
diff --git a/drivers/mxc/security/sahara2/include/sah_interrupt_handler.h b/drivers/mxc/security/sahara2/include/sah_interrupt_handler.h
new file mode 100644
index 000000000000..fc4d77475f85
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sah_interrupt_handler.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+* @file sah_interrupt_handler.h
+*
+* @brief Provides a hardware interrupt handling mechanism for device driver.
+*
+*/
+/******************************************************************************
+*
+* CAUTION:
+*
+* MODIFICATION HISTORY:
+*
+* Date Person Change
+* 30/07/03 MW Initial Creation
+*******************************************************************
+*/
+
+#ifndef SAH_INTERRUPT_HANDLER_H
+#define SAH_INTERRUPT_HANDLER_H
+
+#include <sah_driver_common.h>
+
+/******************************************************************************
+* External function declarations
+******************************************************************************/
+int sah_Intr_Init (wait_queue_head_t *wait_queue);
+void sah_Intr_Release (void);
+
+#endif /* SAH_INTERRUPT_HANDLER_H */
diff --git a/drivers/mxc/security/sahara2/include/sah_kernel.h b/drivers/mxc/security/sahara2/include/sah_kernel.h
new file mode 100644
index 000000000000..e39a61e64430
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sah_kernel.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*
+* @file sah_kernel.h
+*
+* @brief Provides definitions for items that user-space and kernel-space share.
+*/
+/******************************************************************************
+*
+* This file needs to be PORTED to a non-Linux platform
+*/
+
+#ifndef SAH_KERNEL_H
+#define SAH_KERNEL_H
+
+#if defined(__KERNEL__)
+
+#if defined(CONFIG_ARCH_MXC91321) || defined(CONFIG_ARCH_MXC91231) \
+ || defined(CONFIG_ARCH_MX27) || defined(CONFIG_ARCH_MXC92323)
+#include <mach/hardware.h>
+#define SAHA_BASE_ADDR SAHARA_BASE_ADDR
+#define SAHARA_IRQ MXC_INT_SAHARA
+#elif defined(CONFIG_ARCH_MX5)
+#include <mach/hardware.h>
+#define SAHA_BASE_ADDR SAHARA_BASE_ADDR
+#define SAHARA_IRQ MXC_INT_SAHARA_H0
+#else
+#include <mach/mx2.h>
+#endif
+
+#endif /* KERNEL */
+
+/* IO Controls */
+/* The magic number 'k' is reserved for the SPARC architecture. (See <kernel
+ * source root>/Documentation/ioctl-number.txt.
+ *
+ * Note: Numbers 8-13 were used in a previous version of the API and should
+ * be avoided.
+ */
+#define SAH_IOC_MAGIC 'k'
+#define SAHARA_HWRESET _IO(SAH_IOC_MAGIC, 0)
+#define SAHARA_SET_HA _IO(SAH_IOC_MAGIC, 1)
+#define SAHARA_CHK_TEST_MODE _IOR(SAH_IOC_MAGIC,2, int)
+#define SAHARA_DAR _IO(SAH_IOC_MAGIC, 3)
+#define SAHARA_GET_RESULTS _IO(SAH_IOC_MAGIC, 4)
+#define SAHARA_REGISTER _IO(SAH_IOC_MAGIC, 5)
+#define SAHARA_DEREGISTER _IO(SAH_IOC_MAGIC, 6)
+/* 7 */
+/* 8 */
+/* 9 */
+/* 10 */
+/* 11 */
+/* 12 */
+/* 13 */
+
+#define SAHARA_SCC_DROP_PERMS _IOWR(SAH_IOC_MAGIC, 14, scc_partition_info_t)
+#define SAHARA_SCC_SFREE _IOWR(SAH_IOC_MAGIC, 15, scc_partition_info_t)
+
+#define SAHARA_SK_ALLOC _IOWR(SAH_IOC_MAGIC, 16, scc_slot_t)
+#define SAHARA_SK_DEALLOC _IOWR(SAH_IOC_MAGIC, 17, scc_slot_t)
+#define SAHARA_SK_LOAD _IOWR(SAH_IOC_MAGIC, 18, scc_slot_t)
+#define SAHARA_SK_UNLOAD _IOWR(SAH_IOC_MAGIC, 19, scc_slot_t)
+#define SAHARA_SK_SLOT_ENC _IOWR(SAH_IOC_MAGIC, 20, scc_slot_t)
+#define SAHARA_SK_SLOT_DEC _IOWR(SAH_IOC_MAGIC, 21, scc_slot_t)
+
+#define SAHARA_SCC_ENCRYPT _IOWR(SAH_IOC_MAGIC, 22, scc_region_t)
+#define SAHARA_SCC_DECRYPT _IOWR(SAH_IOC_MAGIC, 23, scc_region_t)
+#define SAHARA_GET_CAPS _IOWR(SAH_IOC_MAGIC, 24, fsl_shw_pco_t)
+
+#define SAHARA_SCC_SSTATUS _IOWR(SAH_IOC_MAGIC, 25, scc_partition_info_t)
+
+#define SAHARA_SK_READ _IOWR(SAH_IOC_MAGIC, 29, scc_slot_t)
+
+/*! This is the name that will appear in /proc/interrupts */
+#define SAHARA_NAME "SAHARA"
+
+/*!
+ * SAHARA IRQ number. See page 9-239 of TLICS - Motorola Semiconductors H.K.
+ * TAHITI-Lite IC Specification, Rev 1.1, Feb 2003.
+ *
+ * TAHITI has two blocks of 32 interrupts. The SAHARA IRQ is number 27
+ * in the second block. This means that the SAHARA IRQ is 27 + 32 = 59.
+ */
+#ifndef SAHARA_IRQ
+#define SAHARA_IRQ (27+32)
+#endif
+
+/*!
+ * Device file definition. The #ifndef is here to support the unit test code
+ * by allowing it to specify a different test device.
+ */
+#ifndef SAHARA_DEVICE_SHORT
+#define SAHARA_DEVICE_SHORT "sahara"
+#endif
+
+#ifndef SAHARA_DEVICE
+#define SAHARA_DEVICE "/dev/"SAHARA_DEVICE_SHORT
+#endif
+
+#endif /* SAH_KERNEL_H */
+
+/* End of sah_kernel.h */
diff --git a/drivers/mxc/security/sahara2/include/sah_memory_mapper.h b/drivers/mxc/security/sahara2/include/sah_memory_mapper.h
new file mode 100644
index 000000000000..ddf4a59af540
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sah_memory_mapper.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+* @file sah_memory_mapper.h
+*
+* @brief Re-creates SAHARA data structures in Kernel memory such that they are
+* suitable for DMA.
+*
+*/
+
+#ifndef SAH_MEMORY_MAPPER_H
+#define SAH_MEMORY_MAPPER_H
+
+#include <sah_driver_common.h>
+#include <sah_queue_manager.h>
+
+
+/******************************************************************************
+* External function declarations
+******************************************************************************/
+sah_Head_Desc *sah_Copy_Descriptors(fsl_shw_uco_t * user_ctx,
+ sah_Head_Desc * desc);
+
+sah_Link *sah_Copy_Links(fsl_shw_uco_t * user_ctx, sah_Link * ptr);
+
+sah_Head_Desc *sah_Physicalise_Descriptors(sah_Head_Desc * desc);
+
+sah_Link *sah_Physicalise_Links (sah_Link *ptr);
+
+sah_Head_Desc *sah_DePhysicalise_Descriptors (sah_Head_Desc *desc);
+
+sah_Link *sah_DePhysicalise_Links (sah_Link *ptr);
+
+sah_Link *sah_Make_Links(fsl_shw_uco_t * user_ctx,
+ sah_Link * ptr, sah_Link ** tail);
+
+
+void sah_Destroy_Descriptors (sah_Head_Desc *desc);
+
+void sah_Destroy_Links (sah_Link *link);
+
+void sah_Free_Chained_Descriptors (sah_Head_Desc *desc);
+
+void sah_Free_Chained_Links (sah_Link *link);
+
+int sah_Init_Mem_Map (void);
+
+void sah_Stop_Mem_Map (void);
+
+int sah_Block_Add_Page (int big);
+
+sah_Desc *sah_Alloc_Descriptor (void);
+sah_Head_Desc *sah_Alloc_Head_Descriptor (void);
+void sah_Free_Descriptor (sah_Desc *desc);
+void sah_Free_Head_Descriptor (sah_Head_Desc *desc);
+sah_Link *sah_Alloc_Link (void);
+void sah_Free_Link (sah_Link *link);
+
+void *wire_user_memory(void *address, uint32_t length, void **page_ctx);
+void unwire_user_memory(void **page_ctx);
+
+os_error_code map_user_memory(struct vm_area_struct *vma,
+ uint32_t physical_addr, uint32_t size);
+os_error_code unmap_user_memory(uint32_t user_addr, uint32_t size);
+
+#endif /* SAH_MEMORY_MAPPER_H */
+
+/* End of sah_memory_mapper.h */
diff --git a/drivers/mxc/security/sahara2/include/sah_queue_manager.h b/drivers/mxc/security/sahara2/include/sah_queue_manager.h
new file mode 100644
index 000000000000..895006f7ab91
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sah_queue_manager.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+* @file sah_queue_manager.h
+*
+* @brief This file definitions for the Queue Manager.
+
+* The Queue Manager manages additions and removal from the queue and updates
+* the status of queue entries. It also calls sah_HW_* functions to interact
+* with the hardware.
+*
+*/
+
+#ifndef SAH_QUEUE_MANAGER_H
+#define SAH_QUEUE_MANAGER_H
+
+#include <sah_driver_common.h>
+#include <sah_status_manager.h>
+
+
+/*************************
+* Queue Manager Functions
+*************************/
+fsl_shw_return_t sah_Queue_Manager_Init(void);
+void sah_Queue_Manager_Close(void);
+void sah_Queue_Manager_Reset_Entries(void);
+void sah_Queue_Manager_Append_Entry(sah_Head_Desc *entry);
+void sah_Queue_Manager_Remove_Entry(sah_Head_Desc *entry);
+
+
+/*************************
+* Queue Functions
+*************************/
+sah_Queue *sah_Queue_Construct(void);
+void sah_Queue_Destroy(sah_Queue *this);
+void sah_Queue_Append_Entry(sah_Queue *this, sah_Head_Desc *entry);
+void sah_Queue_Remove_Entry(sah_Queue *this);
+void sah_Queue_Remove_Any_Entry(sah_Queue *this, sah_Head_Desc *entry);
+void sah_postprocess_queue(unsigned long reset_flag);
+
+
+/*************************
+* Misc Releated Functions
+*************************/
+
+int sah_blocking_mode(struct sah_Head_Desc *entry);
+fsl_shw_return_t sah_convert_error_status(uint32_t error_status);
+
+
+#endif /* SAH_QUEUE_MANAGER_H */
+
+/* End of sah_queue_manager.h */
diff --git a/drivers/mxc/security/sahara2/include/sah_status_manager.h b/drivers/mxc/security/sahara2/include/sah_status_manager.h
new file mode 100644
index 000000000000..63660b53c94f
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sah_status_manager.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+* @file sah_status_manager.h
+*
+* @brief SAHARA Status Manager Types and Function Prototypes
+*
+* @author Stuart Holloway (SH)
+*
+*/
+
+#ifndef STATUS_MANAGER_H
+#define STATUS_MANAGER_H
+#include "sah_driver_common.h"
+#include "sahara.h"
+
+
+/******************************************************************************
+* User defined data types
+******************************************************************************/
+/**
+******************************************************************************
+* sah_Execute_Status
+* Types read from SAHARA Status Register, with additional state for Op Status
+******************************************************************************/
+typedef enum sah_Execute_Status
+{
+ /** Sahara is Idle. */
+ SAH_EXEC_IDLE = 0,
+ /** SAHARA is busy performing a resest or processing a decriptor chain. */
+ SAH_EXEC_BUSY = 1,
+ /** An error occurred while SAHARA executed the first descriptor. */
+ SAH_EXEC_ERROR1 = 2,
+ /** SAHARA has failed internally. */
+ SAH_EXEC_FAULT = 3,
+ /** SAHARA has finished processing a descriptor chain and is idle. */
+ SAH_EXEC_DONE1 = 4,
+ /** SAHARA has finished processing a descriptor chain, and is processing a
+ * second chain. */
+ SAH_EXEC_DONE1_BUSY2 = 5,
+ /** SAHARA has finished processing a descriptor chain, but has generated an
+ * error while processing a second descriptor chain. */
+ SAH_EXEC_DONE1_ERROR2 = 6,
+ /** SAHARA has finished two descriptors. */
+ SAH_EXEC_DONE1_DONE2 = 7,
+ /** SAHARA has stopped, and first descriptor has Op Status, not Err */
+ SAH_EXEC_OPSTAT1 = 0x20,
+} sah_Execute_Status;
+
+/**
+ * When this bit is on in a #sah_Execute_Status, it means that DONE1 is true.
+ */
+#define SAH_EXEC_DONE1_BIT 4
+
+/**
+ * Bits which make up the Sahara State
+ */
+#define SAH_EXEC_STATE_MASK 0x00000007
+
+/**
+*******************************************************************************
+* sah_Execute_Error
+* Types read from SAHARA Error Status Register
+******************************************************************************/
+typedef enum sah_Execute_Error
+{
+ /** No Error */
+ SAH_ERR_NONE = 0,
+ /** Header is not valid. */
+ SAH_ERR_HEADER = 1,
+ /** Descriptor length is not correct. */
+ SAH_ERR_DESC_LENGTH = 2,
+ /** Length or pointer field is zero while the other is non-zero. */
+ SAH_ERR_DESC_POINTER = 3,
+ /** Length of the link is not a multiple of 4 and is not the last link */
+ SAH_ERR_LINK_LENGTH = 4,
+ /** The data pointer in a link is zero */
+ SAH_ERR_LINK_POINTER = 5,
+ /** Input Buffer reported an overflow */
+ SAH_ERR_INPUT_BUFFER = 6,
+ /** Output Buffer reported an underflow */
+ SAH_ERR_OUTPUT_BUFFER = 7,
+ /** Incorrect data in output buffer after CHA's has signalled 'done'. */
+ SAH_ERR_OUTPUT_BUFFER_STARVATION = 8,
+ /** Internal Hardware Failure. */
+ SAH_ERR_INTERNAL_STATE = 9,
+ /** Current Descriptor was not legal, but cause is unknown. */
+ SAH_ERR_GENERAL_DESCRIPTOR = 10,
+ /** Reserved pointer fields have been set to 1. */
+ SAH_ERR_RESERVED_FIELDS = 11,
+ /** Descriptor address error */
+ SAH_ERR_DESCRIPTOR_ADDRESS = 12,
+ /** Link address error */
+ SAH_ERR_LINK_ADDRESS = 13,
+ /** Processing error in CHA module */
+ SAH_ERR_CHA = 14,
+ /** Processing error during DMA */
+ SAH_ERR_DMA = 15
+} sah_Execute_Error;
+
+
+/**
+*******************************************************************************
+* sah_CHA_Error_Source
+* Types read from SAHARA Error Status Register for CHA Error Source
+*
+******************************************************************************/
+typedef enum sah_CHA_Error_Source
+{
+ /** No Error indicated in Source CHA Error. */
+ SAH_CHA_NO_ERROR = 0,
+ /** Error in SKHA module. */
+ SAH_CHA_SKHA_ERROR = 1,
+ /** Error in MDHA module. */
+ SAH_CHA_MDHA_ERROR = 2,
+ /** Error in RNG module. */
+ SAH_CHA_RNG_ERROR = 3,
+ /** Error in PKHA module. */
+ SAH_CHA_PKHA_ERROR = 4,
+} sah_CHA_Error_Source;
+
+/**
+******************************************************************************
+* sah_CHA_Error_Status
+* Types read from SAHARA Error Status Register for CHA Error Status
+*
+******************************************************************************/
+typedef enum sah_CHA_Error_Status
+{
+ /** No CHA error detected */
+ SAH_CHA_NO_ERR = 0x000,
+ /** Non-empty input buffer when complete. */
+ SAH_CHA_IP_BUF = 0x001,
+ /** Illegal Address Error. */
+ SAH_CHA_ADD_ERR = 0x002,
+ /** Illegal Mode Error. */
+ SAH_CHA_MODE_ERR = 0x004,
+ /** Illegal Data Size Error. */
+ SAH_CHA_DATA_SIZE_ERR = 0x008,
+ /** Illegal Key Size Error. */
+ SAH_CHA_KEY_SIZE_ERR = 0x010,
+ /** Mode/Context/Key written during processing. */
+ SAH_CHA_PROC_ERR = 0x020,
+ /** Context Read During Processing. */
+ SAH_CHA_CTX_READ_ERR = 0x040,
+ /** Internal Hardware Error. */
+ SAH_CHA_INTERNAL_HW_ERR = 0x080,
+ /** Input Buffer not enabled or underflow. */
+ SAH_CHA_IP_BUFF_ERR = 0x100,
+ /** Output Buffer not enabled or overflow. */
+ SAH_CHA_OP_BUFF_ERR = 0x200,
+ /** DES key parity error (SKHA) */
+ SAH_CHA_DES_KEY_ERR = 0x400,
+ /** Reserved error code. */
+ SAH_CHA_RES = 0x800
+} sah_CHA_Error_Status;
+
+/**
+*****************************************************************************
+* sah_DMA_Error_Status
+* Types read from SAHARA Error Status Register for DMA Error Status
+******************************************************************************/
+typedef enum sah_DMA_Error_Status
+{
+ /** No DMA Error Code. */
+ SAH_DMA_NO_ERR = 0,
+ /** AHB terminated a bus cycle with an error. */
+ SAH_DMA_AHB_ERR = 2,
+ /** Internal IP bus cycle was terminated with an error termination. */
+ SAH_DMA_IP_ERR = 4,
+ /** Parity error detected on DMA command. */
+ SAH_DMA_PARITY_ERR = 6,
+ /** DMA was requested to cross a 256 byte internal address boundary. */
+ SAH_DMA_BOUNDRY_ERR = 8,
+ /** DMA controller is busy */
+ SAH_DMA_BUSY_ERR = 10,
+ /** Memory Bounds Error */
+ SAH_DMA_RESERVED_ERR = 12,
+ /** Internal DMA hardware error detected */
+ SAH_DMA_INT_ERR = 14
+} sah_DMA_Error_Status;
+
+/**
+*****************************************************************************
+* sah_DMA_Error_Size
+* Types read from SAHARA Error Status Register for DMA Error Size
+*
+******************************************************************************/
+typedef enum sah_DMA_Error_Size
+{
+ /** Error during Byte transfer. */
+ SAH_DMA_SIZE_BYTE = 0,
+ /** Error during Half-word transfer. */
+ SAH_DMA_SIZE_HALF_WORD = 1,
+ /** Error during Word transfer. */
+ SAH_DMA_SIZE_WORD = 2,
+ /** Reserved DMA word size. */
+ SAH_DMA_SIZE_RES = 3
+} sah_DMA_Error_Size;
+
+
+
+
+extern bool sah_dpm_flag;
+
+/*************************
+* Status Manager Functions
+*************************/
+
+unsigned long sah_Handle_Interrupt(sah_Execute_Status hw_status);
+sah_Head_Desc *sah_Find_With_State (sah_Queue_Status status);
+int sah_dpm_init(void);
+void sah_dpm_close(void);
+void sah_Queue_Manager_Prime (sah_Head_Desc *entry);
+
+
+#endif /* STATUS_MANAGER_H */
diff --git a/drivers/mxc/security/sahara2/include/sahara.h b/drivers/mxc/security/sahara2/include/sahara.h
new file mode 100644
index 000000000000..c66c125bc129
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sahara.h
@@ -0,0 +1,2265 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file sahara.h
+ *
+ * File which implements the FSL_SHW API when used on Sahara
+ */
+/*!
+ * @if USE_MAINPAGE
+ * @mainpage Sahara2 implemtation of FSL Security Hardware API
+ * @endif
+ *
+ */
+
+#define _DIAG_DRV_IF
+#define _DIAG_SECURITY_FUNC
+#define _DIAG_ADAPTOR
+
+#ifndef SAHARA2_API_H
+#define SAHARA2_API_H
+
+#ifdef DIAG_SECURITY_FUNC
+#include <diagnostic.h>
+#endif /* DIAG_SECURITY_FUNC */
+
+/* This is a Linux flag... ? */
+#ifndef __KERNEL__
+#include <inttypes.h>
+#include <stdlib.h>
+#include <memory.h>
+#else
+#include "portable_os.h"
+#endif
+
+/* This definition may need a new name, and needs to go somewhere which
+ * can determine platform, kernel vs. user, os, etc.
+ */
+#define copy_bytes(out, in, len) memcpy(out, in, len)
+
+/* Does this belong here? */
+#ifndef SAHARA_DEVICE
+#define SAHARA_DEVICE "/dev/sahara"
+#endif
+
+/*!
+*******************************************************************************
+* @defgroup lnkflags Link Flags
+*
+* @brief Flags to show information about link data and link segments
+*
+******************************************************************************/
+/*! @addtogroup lnkflags
+ * @{
+ */
+
+/*!
+*******************************************************************************
+* This flag indicates that the data in a link is owned by the security
+* function component and this memory will be freed by the security function
+* component. To be used as part of the flag field of the sah_Link structure.
+******************************************************************************/
+#define SAH_OWNS_LINK_DATA 0x01
+
+/*!
+*******************************************************************************
+* The data in a link is not owned by the security function component and
+* therefore it will not attempt to free this memory. To be used as part of the
+* flag field of the sah_Link structure.
+******************************************************************************/
+#define SAH_USES_LINK_DATA 0x02
+
+/*!
+*******************************************************************************
+* The data in this link will change when the descriptor gets executed.
+******************************************************************************/
+#define SAH_OUTPUT_LINK 0x04
+
+/*!
+*******************************************************************************
+* The ptr and length in this link are really 'established key' info. They
+* are to be converted to ptr/length before putting on request queue.
+******************************************************************************/
+#define SAH_KEY_IS_HIDDEN 0x08
+
+/*!
+*******************************************************************************
+* The link structure has been appended to the previous one by the driver. It
+* needs to be removed before leaving the driver (and returning to API).
+******************************************************************************/
+#define SAH_REWORKED_LINK 0x10
+
+/*!
+*******************************************************************************
+* The length and data fields of this link contain the slot and user id
+* used to access the SCC stored key
+******************************************************************************/
+#define SAH_STORED_KEY_INFO 0x20
+
+/*!
+*******************************************************************************
+* The Data field points to a physical address, and does not need to be
+* processed by the driver. Honored only in Kernel API.
+******************************************************************************/
+#define SAH_PREPHYS_DATA 0x40
+
+/*!
+*******************************************************************************
+* The link was inserted during the Physicalise procedure. It is tagged so
+* it can be removed during DePhysicalise, thereby returning to the caller an
+* intact chain.
+******************************************************************************/
+#define SAH_LINK_INSERTED_LINK 0x80
+
+/*!
+*******************************************************************************
+* The Data field points to the location of the key, which is in a secure
+* partition held by the user. The memory address needs to be converted to
+* kernel space manually, by looking through the partitions that the user holds.
+******************************************************************************/
+#define SAH_IN_USER_KEYSTORE 0x100
+
+/*!
+*******************************************************************************
+* sah_Link_Flags
+*
+* Type to be used for flags associated with a Link in security function.
+* These flags are used internally by the security function component only.
+*
+* Values defined at @ref lnkflags
+*
+* @brief typedef for flags field of sah_Link
+******************************************************************************/
+typedef uint32_t sah_Link_Flags;
+
+/*
+*******************************************************************************
+* Security Parameters Related Structures
+*
+* All of structures associated with API parameters
+*
+******************************************************************************/
+
+/*
+*******************************************************************************
+* Common Types
+*
+* All of structures used across several classes of crytography
+******************************************************************************/
+
+/*!
+*******************************************************************************
+* @brief Indefinite precision integer used for security operations on SAHARA
+* accelerator. The data will always be in little Endian format.
+******************************************************************************/
+typedef uint8_t *sah_Int;
+
+/*!
+*******************************************************************************
+* @brief Byte array used for block cipher and hash digest/MAC operations on
+* SAHARA accelerator. The Endian format will be as specified by the function
+* using the sah_Oct_Str.
+******************************************************************************/
+typedef uint8_t *sah_Oct_Str;
+
+/*!
+ * A queue of descriptor heads -- used to hold requests waiting for user to
+ * pick up the results. */
+typedef struct sah_Queue {
+ int count; /*!< # entries in queue */
+ struct sah_Head_Desc *head; /*!< first entry in queue */
+ struct sah_Head_Desc *tail; /*!< last entry in queue */
+} sah_Queue;
+
+/******************************************************************************
+ * Enumerations
+ *****************************************************************************/
+/*!
+ * Flags for the state of the User Context Object (#fsl_shw_uco_t).
+ */
+typedef enum fsl_shw_user_ctx_flags_t {
+ /*!
+ * API will block the caller until operation completes. The result will be
+ * available in the return code. If this is not set, user will have to get
+ * results using #fsl_shw_get_results().
+ */
+ FSL_UCO_BLOCKING_MODE = 0x01,
+ /*!
+ * User wants callback (at the function specified with
+ * #fsl_shw_uco_set_callback()) when the operation completes. This flag is
+ * valid only if #FSL_UCO_BLOCKING_MODE is not set.
+ */
+ FSL_UCO_CALLBACK_MODE = 0x02,
+ /*! Do not free descriptor chain after driver (adaptor) finishes */
+ FSL_UCO_SAVE_DESC_CHAIN = 0x04,
+ /*!
+ * User has made at least one request with callbacks requested, so API is
+ * ready to handle others.
+ */
+ FSL_UCO_CALLBACK_SETUP_COMPLETE = 0x08,
+ /*!
+ * (virtual) pointer to descriptor chain is completely linked with physical
+ * (DMA) addresses, ready for the hardware. This flag should not be used
+ * by FSL SHW API programs.
+ */
+ FSL_UCO_CHAIN_PREPHYSICALIZED = 0x10,
+ /*!
+ * The user has changed the context but the changes have not been copied to
+ * the kernel driver.
+ */
+ FSL_UCO_CONTEXT_CHANGED = 0x20,
+ /*! Internal Use. This context belongs to a user-mode API user. */
+ FSL_UCO_USERMODE_USER = 0x40,
+} fsl_shw_user_ctx_flags_t;
+
+/*!
+ * Return code for FSL_SHW library.
+ *
+ * These codes may be returned from a function call. In non-blocking mode,
+ * they will appear as the status in a Result Object.
+ */
+typedef enum fsl_shw_return_t {
+ /*!
+ * No error. As a function return code in Non-blocking mode, this may
+ * simply mean that the operation was accepted for eventual execution.
+ */
+ FSL_RETURN_OK_S = 0,
+ /*! Failure for non-specific reason. */
+ FSL_RETURN_ERROR_S,
+ /*!
+ * Operation failed because some resource was not able to be allocated.
+ */
+ FSL_RETURN_NO_RESOURCE_S,
+ /*! Crypto algorithm unrecognized or improper. */
+ FSL_RETURN_BAD_ALGORITHM_S,
+ /*! Crypto mode unrecognized or improper. */
+ FSL_RETURN_BAD_MODE_S,
+ /*! Flag setting unrecognized or inconsistent. */
+ FSL_RETURN_BAD_FLAG_S,
+ /*! Improper or unsupported key length for algorithm. */
+ FSL_RETURN_BAD_KEY_LENGTH_S,
+ /*! Improper parity in a (DES, TDES) key. */
+ FSL_RETURN_BAD_KEY_PARITY_S,
+ /*!
+ * Improper or unsupported data length for algorithm or internal buffer.
+ */
+ FSL_RETURN_BAD_DATA_LENGTH_S,
+ /*! Authentication / Integrity Check code check failed. */
+ FSL_RETURN_AUTH_FAILED_S,
+ /*! A memory error occurred. */
+ FSL_RETURN_MEMORY_ERROR_S,
+ /*! An error internal to the hardware occurred. */
+ FSL_RETURN_INTERNAL_ERROR_S,
+ /*! ECC detected Point at Infinity */
+ FSL_RETURN_POINT_AT_INFINITY_S,
+ /*! ECC detected No Point at Infinity */
+ FSL_RETURN_POINT_NOT_AT_INFINITY_S,
+ /*! GCD is One */
+ FSL_RETURN_GCD_IS_ONE_S,
+ /*! GCD is not One */
+ FSL_RETURN_GCD_IS_NOT_ONE_S,
+ /*! Candidate is Prime */
+ FSL_RETURN_PRIME_S,
+ /*! Candidate is not Prime */
+ FSL_RETURN_NOT_PRIME_S,
+ /*! N register loaded improperly with even value */
+ FSL_RETURN_EVEN_MODULUS_ERROR_S,
+ /*! Divisor is zero. */
+ FSL_RETURN_DIVIDE_BY_ZERO_ERROR_S,
+ /*! Bad Exponent or Scalar value for Point Multiply */
+ FSL_RETURN_BAD_EXPONENT_ERROR_S,
+ /*! RNG hardware problem. */
+ FSL_RETURN_OSCILLATOR_ERROR_S,
+ /*! RNG hardware problem. */
+ FSL_RETURN_STATISTICS_ERROR_S,
+} fsl_shw_return_t;
+
+/*!
+ * Algorithm Identifier.
+ *
+ * Selection of algorithm will determine how large the block size of the
+ * algorithm is. Context size is the same length unless otherwise specified.
+ * Selection of algorithm also affects the allowable key length.
+ */
+typedef enum fsl_shw_key_alg_t {
+ /*!
+ * Key will be used to perform an HMAC. Key size is 1 to 64 octets. Block
+ * size is 64 octets.
+ */
+ FSL_KEY_ALG_HMAC,
+ /*!
+ * Advanced Encryption Standard (Rijndael). Block size is 16 octets. Key
+ * size is 16 octets. (The single choice of key size is a Sahara platform
+ * limitation.)
+ */
+ FSL_KEY_ALG_AES,
+ /*!
+ * Data Encryption Standard. Block size is 8 octets. Key size is 8
+ * octets.
+ */
+ FSL_KEY_ALG_DES,
+ /*!
+ * 2- or 3-key Triple DES. Block size is 8 octets. Key size is 16 octets
+ * for 2-key Triple DES, and 24 octets for 3-key.
+ */
+ FSL_KEY_ALG_TDES,
+ /*!
+ * ARC4. No block size. Context size is 259 octets. Allowed key size is
+ * 1-16 octets. (The choices for key size are a Sahara platform
+ * limitation.)
+ */
+ FSL_KEY_ALG_ARC4,
+ /*!
+ * Private key of a public-private key-pair. Max is 512 bits...
+ */
+ FSL_KEY_PK_PRIVATE,
+} fsl_shw_key_alg_t;
+
+/*!
+ * Mode selector for Symmetric Ciphers.
+ *
+ * The selection of mode determines how a cryptographic algorithm will be
+ * used to process the plaintext or ciphertext.
+ *
+ * For all modes which are run block-by-block (that is, all but
+ * #FSL_SYM_MODE_STREAM), any partial operations must be performed on a text
+ * length which is multiple of the block size. Except for #FSL_SYM_MODE_CTR,
+ * these block-by-block algorithms must also be passed a total number of octets
+ * which is a multiple of the block size.
+ *
+ * In modes which require that the total number of octets of data be a multiple
+ * of the block size (#FSL_SYM_MODE_ECB and #FSL_SYM_MODE_CBC), and the user
+ * has a total number of octets which are not a multiple of the block size, the
+ * user must perform any necessary padding to get to the correct data length.
+ */
+typedef enum fsl_shw_sym_mode_t {
+ /*!
+ * Stream. There is no associated block size. Any request to process data
+ * may be of any length. This mode is only for ARC4 operations, and is
+ * also the only mode used for ARC4.
+ */
+ FSL_SYM_MODE_STREAM,
+
+ /*!
+ * Electronic Codebook. Each block of data is encrypted/decrypted. The
+ * length of the data stream must be a multiple of the block size. This
+ * mode may be used for DES, 3DES, and AES. The block size is determined
+ * by the algorithm.
+ */
+ FSL_SYM_MODE_ECB,
+ /*!
+ * Cipher-Block Chaining. Each block of data is encrypted/decrypted and
+ * then "chained" with the previous block by an XOR function. Requires
+ * context to start the XOR (previous block). This mode may be used for
+ * DES, 3DES, and AES. The block size is determined by the algorithm.
+ */
+ FSL_SYM_MODE_CBC,
+ /*!
+ * Counter. The counter is encrypted, then XORed with a block of data.
+ * The counter is then incremented (using modulus arithmetic) for the next
+ * block. The final operation may be non-multiple of block size. This mode
+ * may be used for AES. The block size is determined by the algorithm.
+ */
+ FSL_SYM_MODE_CTR,
+} fsl_shw_sym_mode_t;
+
+/*!
+ * Algorithm selector for Cryptographic Hash functions.
+ *
+ * Selection of algorithm determines how large the context and digest will be.
+ * Context is the same size as the digest (resulting hash), unless otherwise
+ * specified.
+ */
+typedef enum fsl_shw_hash_alg_t {
+ /*! MD5 algorithm. Digest is 16 octets. */
+ FSL_HASH_ALG_MD5,
+ /*! SHA-1 (aka SHA or SHA-160) algorithm. Digest is 20 octets. */
+ FSL_HASH_ALG_SHA1,
+ /*!
+ * SHA-224 algorithm. Digest is 28 octets, though context is 32 octets.
+ */
+ FSL_HASH_ALG_SHA224,
+ /*! SHA-256 algorithm. Digest is 32 octets. */
+ FSL_HASH_ALG_SHA256
+} fsl_shw_hash_alg_t;
+
+/*!
+ * The type of Authentication-Cipher function which will be performed.
+ */
+typedef enum fsl_shw_acc_mode_t {
+ /*!
+ * CBC-MAC for Counter. Requires context and modulus. Final operation may
+ * be non-multiple of block size. This mode may be used for AES.
+ */
+ FSL_ACC_MODE_CCM,
+ /*!
+ * SSL mode. Not supported. Combines HMAC and encrypt (or decrypt).
+ * Needs one key object for encryption, another for the HMAC. The usual
+ * hashing and symmetric encryption algorithms are supported.
+ */
+ FSL_ACC_MODE_SSL,
+} fsl_shw_acc_mode_t;
+
+/* REQ-S2LRD-PINTFC-COA-HCO-001 */
+/*!
+ * Flags which control a Hash operation.
+ */
+typedef enum fsl_shw_hash_ctx_flags_t {
+ /*!
+ * Context is empty. Hash is started from scratch, with a
+ * message-processed count of zero.
+ */
+ FSL_HASH_FLAGS_INIT = 0x01,
+ /*!
+ * Retrieve context from hardware after hashing. If used with the
+ * #FSL_HASH_FLAGS_FINALIZE flag, the final digest value will be saved in
+ * the object.
+ */
+ FSL_HASH_FLAGS_SAVE = 0x02,
+ /*! Place context into hardware before hashing. */
+ FSL_HASH_FLAGS_LOAD = 0x04,
+ /*!
+ * PAD message and perform final digest operation. If user message is
+ * pre-padded, this flag should not be used.
+ */
+ FSL_HASH_FLAGS_FINALIZE = 0x08,
+} fsl_shw_hash_ctx_flags_t;
+
+/*!
+ * Flags which control an HMAC operation.
+ *
+ * These may be combined by ORing them together. See #fsl_shw_hmco_set_flags()
+ * and #fsl_shw_hmco_clear_flags().
+ */
+typedef enum fsl_shw_hmac_ctx_flags_t {
+ /*!
+ * Message context is empty. HMAC is started from scratch (with key) or
+ * from precompute of inner hash, depending on whether
+ * #FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT is set.
+ */
+ FSL_HMAC_FLAGS_INIT = 1,
+ /*!
+ * Retrieve ongoing context from hardware after hashing. If used with the
+ * #FSL_HMAC_FLAGS_FINALIZE flag, the final digest value (HMAC) will be
+ * saved in the object.
+ */
+ FSL_HMAC_FLAGS_SAVE = 2,
+ /*! Place ongoing context into hardware before hashing. */
+ FSL_HMAC_FLAGS_LOAD = 4,
+ /*!
+ * PAD message and perform final HMAC operations of inner and outer
+ * hashes.
+ */
+ FSL_HMAC_FLAGS_FINALIZE = 8,
+ /*!
+ * This means that the context contains precomputed inner and outer hash
+ * values.
+ */
+ FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT = 16,
+} fsl_shw_hmac_ctx_flags_t;
+
+/*!
+ * Flags to control use of the #fsl_shw_scco_t.
+ *
+ * These may be ORed together to get the desired effect.
+ * See #fsl_shw_scco_set_flags() and #fsl_shw_scco_clear_flags()
+ */
+typedef enum fsl_shw_sym_ctx_flags_t {
+ /*!
+ * Context is empty. In ARC4, this means that the S-Box needs to be
+ * generated from the key. In #FSL_SYM_MODE_CBC mode, this allows an IV of
+ * zero to be specified. In #FSL_SYM_MODE_CTR mode, it means that an
+ * initial CTR value of zero is desired.
+ */
+ FSL_SYM_CTX_INIT = 1,
+ /*!
+ * Load context from object into hardware before running cipher. In
+ * #FSL_SYM_MODE_CTR mode, this would refer to the Counter Value.
+ */
+ FSL_SYM_CTX_LOAD = 2,
+ /*!
+ * Save context from hardware into object after running cipher. In
+ * #FSL_SYM_MODE_CTR mode, this would refer to the Counter Value.
+ */
+ FSL_SYM_CTX_SAVE = 4,
+ /*!
+ * Context (SBox) is to be unwrapped and wrapped on each use.
+ * This flag is unsupported.
+ * */
+ FSL_SYM_CTX_PROTECT = 8,
+} fsl_shw_sym_ctx_flags_t;
+
+/*!
+ * Flags which describe the state of the #fsl_shw_sko_t.
+ *
+ * These may be ORed together to get the desired effect.
+ * See #fsl_shw_sko_set_flags() and #fsl_shw_sko_clear_flags()
+ */
+typedef enum fsl_shw_key_flags_t {
+ /*! If algorithm is DES or 3DES, do not validate the key parity bits. */
+ FSL_SKO_KEY_IGNORE_PARITY = 1,
+ /*! Clear key is present in the object. */
+ FSL_SKO_KEY_PRESENT = 2,
+ /*!
+ * Key has been established for use. This feature is not available for all
+ * platforms, nor for all algorithms and modes.
+ */
+ FSL_SKO_KEY_ESTABLISHED = 4,
+ /*!
+ * Key intended for user (software) use; can be read cleartext from the
+ * keystore.
+ */
+ FSL_SKO_KEY_SW_KEY = 8,
+} fsl_shw_key_flags_t;
+
+/*!
+ * Type of value which is associated with an established key.
+ */
+typedef uint64_t key_userid_t;
+
+/*!
+ * Flags which describe the state of the #fsl_shw_acco_t.
+ *
+ * The @a FSL_ACCO_CTX_INIT and @a FSL_ACCO_CTX_FINALIZE flags, when used
+ * together, provide for a one-shot operation.
+ */
+typedef enum fsl_shw_auth_ctx_flags_t {
+ /*! Initialize Context(s) */
+ FSL_ACCO_CTX_INIT = 1,
+ /*! Load intermediate context(s). This flag is unsupported. */
+ FSL_ACCO_CTX_LOAD = 2,
+ /*! Save intermediate context(s). This flag is unsupported. */
+ FSL_ACCO_CTX_SAVE = 4,
+ /*! Create MAC during this operation. */
+ FSL_ACCO_CTX_FINALIZE = 8,
+ /*!
+ * Formatting of CCM input data is performed by calls to
+ * #fsl_shw_ccm_nist_format_ctr_and_iv() and
+ * #fsl_shw_ccm_nist_update_ctr_and_iv().
+ */
+ FSL_ACCO_NIST_CCM = 0x10,
+} fsl_shw_auth_ctx_flags_t;
+
+/*!
+ * The operation which controls the behavior of #fsl_shw_establish_key().
+ *
+ * These values are passed to #fsl_shw_establish_key().
+ */
+typedef enum fsl_shw_key_wrap_t {
+ /*! Generate a key from random values. */
+ FSL_KEY_WRAP_CREATE,
+ /*! Use the provided clear key. */
+ FSL_KEY_WRAP_ACCEPT,
+ /*! Unwrap a previously wrapped key. */
+ FSL_KEY_WRAP_UNWRAP
+} fsl_shw_key_wrap_t;
+
+/*!
+ * Modulus Selector for CTR modes.
+ *
+ * The incrementing of the Counter value may be modified by a modulus. If no
+ * modulus is needed or desired for AES, use #FSL_CTR_MOD_128.
+ */
+typedef enum fsl_shw_ctr_mod_t {
+ FSL_CTR_MOD_8, /*!< Run counter with modulus of 2^8. */
+ FSL_CTR_MOD_16, /*!< Run counter with modulus of 2^16. */
+ FSL_CTR_MOD_24, /*!< Run counter with modulus of 2^24. */
+ FSL_CTR_MOD_32, /*!< Run counter with modulus of 2^32. */
+ FSL_CTR_MOD_40, /*!< Run counter with modulus of 2^40. */
+ FSL_CTR_MOD_48, /*!< Run counter with modulus of 2^48. */
+ FSL_CTR_MOD_56, /*!< Run counter with modulus of 2^56. */
+ FSL_CTR_MOD_64, /*!< Run counter with modulus of 2^64. */
+ FSL_CTR_MOD_72, /*!< Run counter with modulus of 2^72. */
+ FSL_CTR_MOD_80, /*!< Run counter with modulus of 2^80. */
+ FSL_CTR_MOD_88, /*!< Run counter with modulus of 2^88. */
+ FSL_CTR_MOD_96, /*!< Run counter with modulus of 2^96. */
+ FSL_CTR_MOD_104, /*!< Run counter with modulus of 2^104. */
+ FSL_CTR_MOD_112, /*!< Run counter with modulus of 2^112. */
+ FSL_CTR_MOD_120, /*!< Run counter with modulus of 2^120. */
+ FSL_CTR_MOD_128 /*!< Run counter with modulus of 2^128. */
+} fsl_shw_ctr_mod_t;
+
+/*!
+ * Permissions flags for Secure Partitions
+ */
+typedef enum fsl_shw_permission_t {
+/*! SCM Access Permission: Do not zeroize/deallocate partition on SMN Fail state */
+ FSL_PERM_NO_ZEROIZE = 0x80000000,
+/*! SCM Access Permission: Enforce trusted key read in */
+ FSL_PERM_TRUSTED_KEY_READ = 0x40000000,
+/*! SCM Access Permission: Ignore Supervisor/User mode in permission determination */
+ FSL_PERM_HD_S = 0x00000800,
+/*! SCM Access Permission: Allow Read Access to Host Domain */
+ FSL_PERM_HD_R = 0x00000400,
+/*! SCM Access Permission: Allow Write Access to Host Domain */
+ FSL_PERM_HD_W = 0x00000200,
+/*! SCM Access Permission: Allow Execute Access to Host Domain */
+ FSL_PERM_HD_X = 0x00000100,
+/*! SCM Access Permission: Allow Read Access to Trusted Host Domain */
+ FSL_PERM_TH_R = 0x00000040,
+/*! SCM Access Permission: Allow Write Access to Trusted Host Domain */
+ FSL_PERM_TH_W = 0x00000020,
+/*! SCM Access Permission: Allow Read Access to Other/World Domain */
+ FSL_PERM_OT_R = 0x00000004,
+/*! SCM Access Permission: Allow Write Access to Other/World Domain */
+ FSL_PERM_OT_W = 0x00000002,
+/*! SCM Access Permission: Allow Execute Access to Other/World Domain */
+ FSL_PERM_OT_X = 0x00000001,
+} fsl_shw_permission_t;
+
+typedef enum fsl_shw_cypher_mode_t {
+ FSL_SHW_CYPHER_MODE_ECB = 1, /*!< ECB mode */
+ FSL_SHW_CYPHER_MODE_CBC = 2, /*!< CBC mode */
+} fsl_shw_cypher_mode_t;
+
+typedef enum fsl_shw_pf_key_t {
+ FSL_SHW_PF_KEY_IIM, /*!< Present fused IIM key */
+ FSL_SHW_PF_KEY_PRG, /*!< Present Program key */
+ FSL_SHW_PF_KEY_IIM_PRG, /*!< Present IIM ^ Program key */
+ FSL_SHW_PF_KEY_IIM_RND, /*!< Present Random key */
+ FSL_SHW_PF_KEY_RND, /*!< Present IIM ^ Random key */
+} fsl_shw_pf_key_t;
+
+typedef enum fsl_shw_tamper_t {
+ FSL_SHW_TAMPER_NONE, /*!< No error detected */
+ FSL_SHW_TAMPER_WTD, /*!< wire-mesh tampering det */
+ FSL_SHW_TAMPER_ETBD, /*!< ext tampering det: input B */
+ FSL_SHW_TAMPER_ETAD, /*!< ext tampering det: input A */
+ FSL_SHW_TAMPER_EBD, /*!< external boot detected */
+ FSL_SHW_TAMPER_SAD, /*!< security alarm detected */
+ FSL_SHW_TAMPER_TTD, /*!< temperature tampering det */
+ FSL_SHW_TAMPER_CTD, /*!< clock tampering det */
+ FSL_SHW_TAMPER_VTD, /*!< voltage tampering det */
+ FSL_SHW_TAMPER_MCO, /*!< monotonic counter overflow */
+ FSL_SHW_TAMPER_TCO, /*!< time counter overflow */
+} fsl_shw_tamper_t;
+
+/******************************************************************************
+ * Data Structures
+ *****************************************************************************/
+
+/*!
+ *
+ * @brief Structure type for descriptors
+ *
+ * The first five fields are passed to the hardware.
+ *
+ *****************************************************************************/
+#ifndef USE_NEW_PTRS /* Experimental */
+
+typedef struct sah_Desc {
+ uint32_t header; /*!< descriptor header value */
+ uint32_t len1; /*!< number of data bytes in 'ptr1' buffer */
+ void *ptr1; /*!< pointer to first sah_Link structure */
+ uint32_t len2; /*!< number of data bytes in 'ptr2' buffer */
+ void *ptr2; /*!< pointer to second sah_Link structure */
+ struct sah_Desc *next; /*!< pointer to next descriptor */
+#ifdef __KERNEL__ /* This needs a better test */
+ /* These two must be last. See sah_Copy_Descriptors */
+ struct sah_Desc *virt_addr; /*!< Virtual (kernel) address of this
+ descriptor. */
+ dma_addr_t dma_addr; /*!< Physical (bus) address of this
+ descriptor. */
+ void *original_ptr1; /*!< user's pointer to ptr1 */
+ void *original_ptr2; /*!< user's pointer to ptr2 */
+ struct sah_Desc *original_next; /*!< user's pointer to next */
+#endif
+} sah_Desc;
+
+#else
+
+typedef struct sah_Desc {
+ uint32_t header; /*!< descriptor header value */
+ uint32_t len1; /*!< number of data bytes in 'ptr1' buffer */
+ uint32_t hw_ptr1; /*!< pointer to first sah_Link structure */
+ uint32_t len2; /*!< number of data bytes in 'ptr2' buffer */
+ uint32_t hw_ptr2; /*!< pointer to second sah_Link structure */
+ uint32_t hw_next; /*!< pointer to next descriptor */
+ struct sah_Link *ptr1; /*!< (virtual) pointer to first sah_Link structure */
+ struct sah_Link *ptr2; /*!< (virtual) pointer to first sah_Link structure */
+ struct sah_Desc *next; /*!< (virtual) pointer to next descriptor */
+#ifdef __KERNEL__ /* This needs a better test */
+ /* These two must be last. See sah_Copy_Descriptors */
+ struct sah_Desc *virt_addr; /*!< Virtual (kernel) address of this
+ descriptor. */
+ dma_addr_t dma_addr; /*!< Physical (bus) address of this
+ descriptor. */
+#endif
+} sah_Desc;
+
+#endif
+
+/*!
+*******************************************************************************
+* @brief The first descriptor in a chain
+******************************************************************************/
+typedef struct sah_Head_Desc {
+ sah_Desc desc; /*!< whole struct - must be first */
+ struct fsl_shw_uco_t *user_info; /*!< where result pool lives */
+ uint32_t user_ref; /*!< at time of request */
+ uint32_t uco_flags; /*!< at time of request */
+ uint32_t status; /*!< Status of queue entry */
+ uint32_t error_status; /*!< If error, register from Sahara */
+ uint32_t fault_address; /*!< If error, register from Sahara */
+ uint32_t op_status; /*!< If error, register from Sahara */
+ fsl_shw_return_t result; /*!< Result of running descriptor */
+ struct sah_Head_Desc *next; /*!< Next in queue */
+ struct sah_Head_Desc *prev; /*!< previous in queue */
+ struct sah_Head_Desc *user_desc; /*!< For API async get_results */
+ void *out1_ptr; /*!< For async post-processing */
+ void *out2_ptr; /*!< For async post-processing */
+ uint32_t out_len; /*!< For async post-processing */
+} sah_Head_Desc;
+
+/*!
+ * @brief Structure type for links
+ *
+ * The first three fields are used by hardware.
+ *****************************************************************************/
+#ifndef USE_NEW_PTRS
+
+typedef struct sah_Link {
+ size_t len; /*!< len of 'data' buffer in bytes */
+ uint8_t *data; /*!< buffer to store data */
+ struct sah_Link *next; /*!< pointer to the next sah_Link storing
+ * data */
+ sah_Link_Flags flags; /*!< indicates the component that created the
+ * data buffer. Security Function internal
+ * information */
+ key_userid_t ownerid; /*!< Auth code for established key */
+ uint32_t slot; /*!< Location of the the established key */
+#ifdef __KERNEL__ /* This needs a better test */
+ /* These two elements must be last. See sah_Copy_Links() */
+ struct sah_Link *virt_addr;
+ dma_addr_t dma_addr;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ struct page *vm_info;
+#endif
+ uint8_t *original_data; /*!< user's version of data pointer */
+ struct sah_Link *original_next; /*!< user's version of next pointer */
+#ifdef SAH_COPY_DATA
+ uint8_t *copy_data; /*!< Virtual address of acquired buffer */
+#endif
+#endif /* kernel-only */
+} sah_Link;
+
+#else
+
+typedef struct sah_Link {
+ /*! len of 'data' buffer in bytes */
+ size_t len;
+ /*! buffer to store data */
+ uint32_t hw_data;
+ /*! Physical address */
+ uint32_t hw_next;
+ /*!
+ * indicates the component that created the data buffer. Security Function
+ * internal information
+ */
+ sah_Link_Flags flags;
+ /*! (virtual) pointer to data */
+ uint8_t *data;
+ /*! (virtual) pointer to the next sah_Link storing data */
+ struct sah_Link *next;
+ /*! Auth code for established key */
+ key_userid_t ownerid;
+ /*! Location of the the established key */
+ uint32_t slot;
+#ifdef __KERNEL__ /* This needs a better test */
+ /* These two elements must be last. See sah_Copy_Links() */
+ struct sah_Link *virt_addr;
+ dma_addr_t dma_addr;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ struct page *vm_info;
+#endif
+#endif /* kernel-only */
+} sah_Link;
+
+#endif
+
+/*!
+ * Initialization Object
+ */
+typedef struct fsl_sho_ibo_t {
+} fsl_sho_ibo_t;
+
+/* Imported from Sahara1 driver -- is it needed forever? */
+/*!
+*******************************************************************************
+* FIELDS
+*
+* void * ref - parameter to be passed into the memory function calls
+*
+* void * (*malloc)(void *ref, size_t n) - pointer to user's malloc function
+*
+* void (*free)(void *ref, void *ptr) - pointer to user's free function
+*
+* void * (*memcpy)(void *ref, void *dest, const void *src, size_t n) -
+* pointer to user's memcpy function
+*
+* void * (*memset)(void *ref, void *ptr, int ch, size_t n) - pointer to
+* user's memset function
+*
+* @brief Structure for API memory utilities
+******************************************************************************/
+typedef struct sah_Mem_Util {
+ /*! Who knows. Vestigial. */
+ void *mu_ref;
+ /*! Acquire buffer of size n bytes */
+ void *(*mu_malloc) (void *ref, size_t n);
+ /*! Acquire a sah_Head_Desc */
+ sah_Head_Desc *(*mu_alloc_head_desc) (void *ref);
+ /* Acquire a sah_Desc */
+ sah_Desc *(*mu_alloc_desc) (void *ref);
+ /* Acquire a sah_Link */
+ sah_Link *(*mu_alloc_link) (void *ref);
+ /*! Free buffer at ptr */
+ void (*mu_free) (void *ref, void *ptr);
+ /*! Free sah_Head_Desc at ptr */
+ void (*mu_free_head_desc) (void *ref, sah_Head_Desc * ptr);
+ /*! Free sah_Desc at ptr */
+ void (*mu_free_desc) (void *ref, sah_Desc * ptr);
+ /*! Free sah_Link at ptr */
+ void (*mu_free_link) (void *ref, sah_Link * ptr);
+ /*! Funciton which will copy n bytes from src to dest */
+ void *(*mu_memcpy) (void *ref, void *dest, const void *src, size_t n);
+ /*! Set all n bytes of ptr to ch */
+ void *(*mu_memset) (void *ref, void *ptr, int ch, size_t n);
+} sah_Mem_Util;
+
+/*!
+ * Secure Partition information
+ *
+ * This holds the context to a single secure partition owned by the user. It
+ * is only available in the kernel version of the User Context Object.
+ */
+typedef struct fsl_shw_spo_t {
+ uint32_t user_base; /*!< Base address (user virtual) */
+ void *kernel_base; /*!< Base address (kernel virtual) */
+ struct fsl_shw_spo_t *next; /*!< Pointer to the next partition
+ owned by the user. NULL if this
+ is the last partition. */
+} fsl_shw_spo_t;
+
+/* REQ-S2LRD-PINTFC-COA-UCO-001 */
+/*!
+ * User Context Object
+ */
+typedef struct fsl_shw_uco_t {
+ int sahara_openfd; /*!< this should be kernel-only?? */
+ sah_Mem_Util *mem_util; /*!< Memory utility fns */
+ uint32_t user_ref; /*!< User's reference */
+ void (*callback) (struct fsl_shw_uco_t * uco); /*!< User's callback fn */
+ uint32_t flags; /*!< from fsl_shw_user_ctx_flags_t */
+ unsigned pool_size; /*!< maximum size of user pool */
+#ifdef __KERNEL__
+ sah_Queue result_pool; /*!< where non-blocking results go */
+ os_process_handle_t process; /*!< remember for signalling User mode */
+ fsl_shw_spo_t *partition; /*!< chain of secure partitions owned by
+ the user */
+#else
+ struct fsl_shw_uco_t *next; /*!< To allow user-mode chaining of contexts,
+ for signalling. */
+#endif
+} fsl_shw_uco_t;
+
+/* REQ-S2LRD-PINTFC-API-GEN-006 ?? */
+/*!
+ * Result object
+ */
+typedef struct fsl_shw_result_t {
+ uint32_t user_ref;
+ fsl_shw_return_t code;
+ uint32_t detail1;
+ uint32_t detail2;
+ sah_Head_Desc *user_desc;
+} fsl_shw_result_t;
+
+/*!
+ * Keystore Object
+ */
+typedef struct fsl_shw_kso_t {
+#ifdef __KERNEL__
+ os_lock_t lock; /*!< Pointer to lock that controls access to
+ the keystore. */
+#endif
+ void *user_data; /*!< Pointer to user structure that handles
+ the internals of the keystore. */
+ fsl_shw_return_t(*data_init) (fsl_shw_uco_t * user_ctx,
+ void **user_data);
+ void (*data_cleanup) (fsl_shw_uco_t * user_ctx, void **user_data);
+ fsl_shw_return_t(*slot_verify_access) (void *user_data,
+ uint64_t owner_id,
+ uint32_t slot);
+ fsl_shw_return_t(*slot_alloc) (void *user_data, uint32_t size_bytes,
+ uint64_t owner_id, uint32_t * slot);
+ fsl_shw_return_t(*slot_dealloc) (void *user_data, uint64_t owner_id,
+ uint32_t slot);
+ void *(*slot_get_address) (void *user_data, uint32_t slot);
+ uint32_t(*slot_get_base) (void *user_data, uint32_t slot);
+ uint32_t(*slot_get_offset) (void *user_data, uint32_t slot);
+ uint32_t(*slot_get_slot_size) (void *user_data, uint32_t slot);
+} fsl_shw_kso_t;
+
+/* REQ-S2LRD-PINTFC-COA-SKO-001 */
+/*!
+ * Secret Key Context Object
+ */
+typedef struct fsl_shw_sko_t {
+ uint32_t flags;
+ fsl_shw_key_alg_t algorithm;
+ key_userid_t userid;
+ uint32_t handle;
+ uint16_t key_length;
+ uint8_t key[64];
+ struct fsl_shw_kso_t *keystore; /*!< If present, key is in keystore */
+} fsl_shw_sko_t;
+
+/* REQ-S2LRD-PINTFC-COA-CO-001 */
+/*!
+ * @brief Platform Capability Object
+ */
+typedef struct fsl_shw_pco_t { /* Consider turning these constants into symbols */
+ int api_major;
+ int api_minor;
+ int driver_major;
+ int driver_minor;
+ fsl_shw_key_alg_t sym_algorithms[4];
+ fsl_shw_sym_mode_t sym_modes[4];
+ fsl_shw_hash_alg_t hash_algorithms[4];
+ uint8_t sym_support[5][4]; /* indexed by key alg then mode */
+
+ int scc_driver_major;
+ int scc_driver_minor;
+ int scm_version; /*!< Version from SCM Configuration register */
+ int smn_version; /*!< Version from SMN Status register */
+ int block_size_bytes; /*!< Number of bytes per block of RAM; also
+ block size of the crypto algorithm. */
+ union {
+ struct {
+ int black_ram_size_blocks; /*!< Number of blocks of Black RAM */
+ int red_ram_size_blocks; /*!< Number of blocks of Red RAM */
+ } scc_info;
+ struct {
+ int partition_size_bytes; /*!< Number of bytes in each partition */
+ int partition_count; /*!< Number of partitions on this platform */
+ } scc2_info;
+ };
+} fsl_shw_pco_t;
+
+/* REQ-S2LRD-PINTFC-COA-HCO-001 */
+/*!
+ * Hash Context Object
+ */
+typedef struct fsl_shw_hco_t { /* fsl_shw_hash_context_object */
+ fsl_shw_hash_alg_t algorithm;
+ uint32_t flags;
+ uint8_t digest_length; /* in bytes */
+ uint8_t context_length; /* in bytes */
+ uint8_t context_register_length; /* in bytes */
+ uint32_t context[9]; /* largest digest + msg size */
+} fsl_shw_hco_t;
+
+/*!
+ * HMAC Context Object
+ */
+typedef struct fsl_shw_hmco_t { /* fsl_shw_hmac_context_object */
+ fsl_shw_hash_alg_t algorithm;
+ uint32_t flags;
+ uint8_t digest_length; /*!< in bytes */
+ uint8_t context_length; /*!< in bytes */
+ uint8_t context_register_length; /*!< in bytes */
+ uint32_t ongoing_context[9]; /*!< largest digest + msg
+ size */
+ uint32_t inner_precompute[9]; /*!< largest digest + msg
+ size */
+ uint32_t outer_precompute[9]; /*!< largest digest + msg
+ size */
+} fsl_shw_hmco_t;
+
+/* REQ-S2LRD-PINTFC-COA-SCCO-001 */
+/*!
+ * Symmetric Crypto Context Object Context Object
+ */
+typedef struct fsl_shw_scco_t {
+ uint32_t flags;
+ unsigned block_size_bytes; /* double duty block&ctx size */
+ fsl_shw_sym_mode_t mode;
+ /* Could put modulus plus 16-octet context in union with arc4
+ sbox+ptrs... */
+ fsl_shw_ctr_mod_t modulus_exp;
+ uint8_t context[259];
+} fsl_shw_scco_t;
+
+/*!
+ * Authenticate-Cipher Context Object
+
+ * An object for controlling the function of, and holding information about,
+ * data for the authenticate-cipher functions, #fsl_shw_gen_encrypt() and
+ * #fsl_shw_auth_decrypt().
+ */
+typedef struct fsl_shw_acco_t {
+ uint32_t flags; /*!< See #fsl_shw_auth_ctx_flags_t for
+ meanings */
+ fsl_shw_acc_mode_t mode; /*!< CCM only */
+ uint8_t mac_length; /*!< User's value for length */
+ unsigned q_length; /*!< NIST parameter - */
+ fsl_shw_scco_t cipher_ctx_info; /*!< For running
+ encrypt/decrypt. */
+ union {
+ fsl_shw_scco_t CCM_ctx_info; /*!< For running the CBC in
+ AES-CCM. */
+ fsl_shw_hco_t hash_ctx_info; /*!< For running the hash */
+ } auth_info; /*!< "auth" info struct */
+ uint8_t unencrypted_mac[16]; /*!< max block size... */
+} fsl_shw_acco_t;
+
+/*!
+ * Used by Sahara API to retrieve completed non-blocking results.
+ */
+typedef struct sah_results {
+ unsigned requested; /*!< number of results requested */
+ unsigned *actual; /*!< number of results obtained */
+ fsl_shw_result_t *results; /*!< pointer to memory to hold results */
+} sah_results;
+
+/*!
+ * @typedef scc_partition_status_t
+ */
+/*! Partition status information. */
+typedef enum fsl_shw_partition_status_t {
+ FSL_PART_S_UNUSABLE, /*!< Partition not implemented */
+ FSL_PART_S_UNAVAILABLE, /*!< Partition owned by other host */
+ FSL_PART_S_AVAILABLE, /*!< Partition available */
+ FSL_PART_S_ALLOCATED, /*!< Partition owned by host but not engaged
+ */
+ FSL_PART_S_ENGAGED, /*!< Partition owned by host and engaged */
+} fsl_shw_partition_status_t;
+
+/******************************************************************************
+ * Access Macros for Objects
+ *****************************************************************************/
+/*!
+ * Get FSL SHW API version
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] pcmajor A pointer to where the major version
+ * of the API is to be stored.
+ * @param[out] pcminor A pointer to where the minor version
+ * of the API is to be stored.
+ */
+#define fsl_shw_pco_get_version(pcobject, pcmajor, pcminor) \
+{ \
+ *(pcmajor) = (pcobject)->api_major; \
+ *(pcminor) = (pcobject)->api_minor; \
+}
+
+/*!
+ * Get underlying driver version.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] pcmajor A pointer to where the major version
+ * of the driver is to be stored.
+ * @param[out] pcminor A pointer to where the minor version
+ * of the driver is to be stored.
+ */
+#define fsl_shw_pco_get_driver_version(pcobject, pcmajor, pcminor) \
+{ \
+ *(pcmajor) = (pcobject)->driver_major; \
+ *(pcminor) = (pcobject)->driver_minor; \
+}
+
+/*!
+ * Get list of symmetric algorithms supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] pcalgorithms A pointer to where to store the location of
+ * the list of algorithms.
+ * @param[out] pcacount A pointer to where to store the number of
+ * algorithms in the list at @a algorithms.
+ */
+#define fsl_shw_pco_get_sym_algorithms(pcobject, pcalgorithms, pcacount) \
+{ \
+ *(pcalgorithms) = (pcobject)->sym_algorithms; \
+ *(pcacount) = sizeof((pcobject)->sym_algorithms)/4; \
+}
+
+/*!
+ * Get list of symmetric modes supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] gsmodes A pointer to where to store the location of
+ * the list of modes.
+ * @param[out] gsacount A pointer to where to store the number of
+ * algorithms in the list at @a modes.
+ */
+#define fsl_shw_pco_get_sym_modes(pcobject, gsmodes, gsacount) \
+{ \
+ *(gsmodes) = (pcobject)->sym_modes; \
+ *(gsacount) = sizeof((pcobject)->sym_modes)/4; \
+}
+
+/*!
+ * Get list of hash algorithms supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] gsalgorithms A pointer which will be set to the list of
+ * algorithms.
+ * @param[out] gsacount The number of algorithms in the list at @a
+ * algorithms.
+ */
+#define fsl_shw_pco_get_hash_algorithms(pcobject, gsalgorithms, gsacount) \
+{ \
+ *(gsalgorithms) = (pcobject)->hash_algorithms; \
+ *(gsacount) = sizeof((pcobject)->hash_algorithms)/4; \
+}
+
+/*!
+ * Determine whether the combination of a given symmetric algorithm and a given
+ * mode is supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param pcalg A Symmetric Cipher algorithm.
+ * @param pcmode A Symmetric Cipher mode.
+ *
+ * @return 0 if combination is not supported, non-zero if supported.
+ */
+#define fsl_shw_pco_check_sym_supported(pcobject, pcalg, pcmode) \
+ ((pcobject)->sym_support[pcalg][pcmode])
+
+/*!
+ * Determine whether a given Encryption-Authentication mode is supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param pcmode The Authentication mode.
+ *
+ * @return 0 if mode is not supported, non-zero if supported.
+ */
+#define fsl_shw_pco_check_auth_supported(pcobject, pcmode) \
+ ((pcmode == FSL_ACC_MODE_CCM) ? 1 : 0)
+
+/*!
+ * Determine whether Black Keys (key establishment / wrapping) is supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ *
+ * @return 0 if wrapping is not supported, non-zero if supported.
+ */
+#define fsl_shw_pco_check_black_key_supported(pcobject) \
+ 1
+
+/*!
+ * Determine whether Programmed Key features are available
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ *
+ * @return 1 if Programmed Key features are available, otherwise zero.
+ */
+#define fsl_shw_pco_check_pk_supported(pcobject) \
+ 0
+
+/*!
+ * Determine whether Software Key features are available
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ *
+ * @return 1 if Software key features are available, otherwise zero.
+ */
+#define fsl_shw_pco_check_sw_keys_supported(pcobject) \
+ 0
+
+/*!
+ * Get FSL SHW SCC driver version
+ *
+ * @param pcobject The Platform Capabilities Object to query.
+ * @param[out] pcmajor A pointer to where the major version
+ * of the SCC driver is to be stored.
+ * @param[out] pcminor A pointer to where the minor version
+ * of the SCC driver is to be stored.
+ */
+#define fsl_shw_pco_get_scc_driver_version(pcobject, pcmajor, pcminor) \
+{ \
+ *(pcmajor) = (pcobject)->scc_driver_major; \
+ *(pcminor) = (pcobject)->scc_driver_minor; \
+}
+
+/*!
+ * Get SCM hardware version
+ *
+ * @param pcobject The Platform Capabilities Object to query.
+ * @return The SCM hardware version
+ */
+#define fsl_shw_pco_get_scm_version(pcobject) \
+ ((pcobject)->scm_version)
+
+/*!
+ * Get SMN hardware version
+ *
+ * @param pcobject The Platform Capabilities Object to query.
+ * @return The SMN hardware version
+ */
+#define fsl_shw_pco_get_smn_version(pcobject) \
+ ((pcobject)->smn_version)
+
+/*!
+ * Get the size of an SCM block, in bytes
+ *
+ * @param pcobject The Platform Capabilities Object to query.
+ * @return The size of an SCM block, in bytes.
+ */
+#define fsl_shw_pco_get_scm_block_size(pcobject) \
+ ((pcobject)->block_size_bytes)
+
+/*!
+ * Get size of Black and Red RAM memory
+ *
+ * @param pcobject The Platform Capabilities Object to query.
+ * @param[out] black_size A pointer to where the size of the Black RAM, in
+ * blocks, is to be placed.
+ * @param[out] red_size A pointer to where the size of the Red RAM, in
+ * blocks, is to be placed.
+ */
+#define fsl_shw_pco_get_smn_size(pcobject, black_size, red_size) \
+{ \
+ if ((pcobject)->scm_version == 1) { \
+ *(black_size) = (pcobject)->scc_info.black_ram_size_blocks; \
+ *(red_size) = (pcobject)->scc_info.red_ram_size_blocks; \
+ } else { \
+ *(black_size) = 0; \
+ *(red_size) = 0; \
+ } \
+}
+
+/*!
+ * Determine whether Secure Partitions are supported
+ *
+ * @param pcobject The Platform Capabilities Object to query.
+ *
+ * @return 0 if secure partitions are not supported, non-zero if supported.
+ */
+#define fsl_shw_pco_check_spo_supported(pcobject) \
+ ((pcobject)->scm_version == 2)
+
+/*!
+ * Get the size of a Secure Partitions
+ *
+ * @param pcobject The Platform Capabilities Object to query.
+ *
+ * @return Partition size, in bytes. 0 if Secure Partitions not supported.
+ */
+#define fsl_shw_pco_get_spo_size_bytes(pcobject) \
+ (((pcobject)->scm_version == 2) ? \
+ ((pcobject)->scc2_info.partition_size_bytes) : 0 )
+
+/*!
+ * Get the number of Secure Partitions on this platform
+ *
+ * @param pcobject The Platform Capabilities Object to query.
+ *
+ * @return Number of partitions. 0 if Secure Paritions not supported. Note
+ * that this returns the total number of partitions, not all may be
+ * available to the user.
+ */
+#define fsl_shw_pco_get_spo_count(pcobject) \
+ (((pcobject)->scm_version == 2) ? \
+ ((pcobject)->scc2_info.partition_count) : 0 )
+
+/*!
+ * Initialize a User Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the User Context Object to initial values, and set the size
+ * of the results pool. The mode will be set to a default of
+ * #FSL_UCO_BLOCKING_MODE.
+ *
+ * When using non-blocking operations, this sets the maximum number of
+ * operations which can be outstanding. This number includes the counts of
+ * operations waiting to start, operation(s) being performed, and results which
+ * have not been retrieved.
+ *
+ * Changes to this value are ignored once user registration has completed. It
+ * should be set to 1 if only blocking operations will ever be performed.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param usize The maximum number of operations which can be
+ * outstanding.
+ */
+#ifdef __KERNEL__
+#define fsl_shw_uco_init(ucontext, usize) \
+{ \
+ (ucontext)->pool_size = usize; \
+ (ucontext)->flags = FSL_UCO_BLOCKING_MODE; \
+ (ucontext)->sahara_openfd = -1; \
+ (ucontext)->mem_util = NULL; \
+ (ucontext)->partition = NULL; \
+ (ucontext)->callback = NULL; \
+}
+#else
+#define fsl_shw_uco_init(ucontext, usize) \
+{ \
+ (ucontext)->pool_size = usize; \
+ (ucontext)->flags = FSL_UCO_BLOCKING_MODE; \
+ (ucontext)->sahara_openfd = -1; \
+ (ucontext)->mem_util = NULL; \
+ (ucontext)->callback = NULL; \
+}
+#endif
+
+/*!
+ * Set the User Reference for the User Context.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param uref A value which will be passed back with a result.
+ */
+#define fsl_shw_uco_set_reference(ucontext, uref) \
+ (ucontext)->user_ref = uref
+
+/*!
+ * Set the User Reference for the User Context.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param ucallback The function the API will invoke when an operation
+ * completes.
+ */
+#define fsl_shw_uco_set_callback(ucontext, ucallback) \
+ (ucontext)->callback = ucallback
+
+/*!
+ * Set flags in the User Context.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param uflags ORed values from #fsl_shw_user_ctx_flags_t.
+ */
+#define fsl_shw_uco_set_flags(ucontext, uflags) \
+ (ucontext)->flags |= (uflags)
+
+/*!
+ * Clear flags in the User Context.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param uflags ORed values from #fsl_shw_user_ctx_flags_t.
+ */
+#define fsl_shw_uco_clear_flags(ucontext, uflags) \
+ (ucontext)->flags &= ~(uflags)
+
+/*!
+ * Retrieve the reference value from a Result Object.
+ *
+ * @param robject The result object to query.
+ *
+ * @return The reference associated with the request.
+ */
+#define fsl_shw_ro_get_reference(robject) \
+ (robject)->user_ref
+
+/*!
+ * Retrieve the status code from a Result Object.
+ *
+ * @param robject The result object to query.
+ *
+ * @return The status of the request.
+ */
+#define fsl_shw_ro_get_status(robject) \
+ (robject)->code
+
+/*!
+ * Initialize a Secret Key Object.
+ *
+ * This function must be called before performing any other operation with
+ * the Object.
+ *
+ * @param skobject The Secret Key Object to be initialized.
+ * @param skalgorithm DES, AES, etc.
+ *
+ */
+#define fsl_shw_sko_init(skobject,skalgorithm) \
+{ \
+ (skobject)->algorithm = skalgorithm; \
+ (skobject)->flags = 0; \
+ (skobject)->keystore = NULL; \
+}
+
+/*!
+ * Initialize a Secret Key Object to use a Platform Key register.
+ *
+ * This function must be called before performing any other operation with
+ * the Object. INVALID on this platform.
+ *
+ * @param skobject The Secret Key Object to be initialized.
+ * @param skalgorithm DES, AES, etc.
+ * @param skhwkey one of the fsl_shw_pf_key_t values.
+ *
+ */
+#define fsl_shw_sko_init_pf_key(skobject,skalgorithm,skhwkey) \
+{ \
+ (skobject)->algorithm = -1; \
+ (skobject)->flags = -1; \
+ (skobject)->keystore = NULL; \
+}
+
+/*!
+ * Store a cleartext key in the key object.
+ *
+ * This has the side effect of setting the #FSL_SKO_KEY_PRESENT flag and
+ * resetting the #FSL_SKO_KEY_ESTABLISHED flag.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skkey A pointer to the beginning of the key.
+ * @param skkeylen The length, in octets, of the key. The value should be
+ * appropriate to the key size supported by the algorithm.
+ * 64 octets is the absolute maximum value allowed for this
+ * call.
+ */
+#define fsl_shw_sko_set_key(skobject, skkey, skkeylen) \
+{ \
+ (skobject)->key_length = skkeylen; \
+ copy_bytes((skobject)->key, skkey, skkeylen); \
+ (skobject)->flags |= FSL_SKO_KEY_PRESENT; \
+ (skobject)->flags &= ~FSL_SKO_KEY_ESTABLISHED; \
+}
+
+/*!
+ * Set a size for the key.
+ *
+ * This function would normally be used when the user wants the key to be
+ * generated from a random source.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skkeylen The length, in octets, of the key. The value should be
+ * appropriate to the key size supported by the algorithm.
+ * 64 octets is the absolute maximum value allowed for this
+ * call.
+ */
+#define fsl_shw_sko_set_key_length(skobject, skkeylen) \
+ (skobject)->key_length = skkeylen;
+
+/*!
+ * Set the User ID associated with the key.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skuserid The User ID to identify authorized users of the key.
+ */
+#define fsl_shw_sko_set_user_id(skobject, skuserid) \
+ (skobject)->userid = (skuserid)
+
+/*!
+ * Establish a user Keystore to hold the key.
+ */
+#define fsl_shw_sko_set_keystore(skobject, user_keystore) \
+ (skobject)->keystore = (user_keystore)
+
+/*!
+ * Set the establish key handle into a key object.
+ *
+ * The @a userid field will be used to validate the access to the unwrapped
+ * key. This feature is not available for all platforms, nor for all
+ * algorithms and modes.
+ *
+ * The #FSL_SKO_KEY_ESTABLISHED will be set (and the #FSL_SKO_KEY_PRESENT flag
+ * will be cleared).
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skuserid The User ID to verify this user is an authorized user of
+ * the key.
+ * @param skhandle A @a handle from #fsl_shw_sko_get_established_info.
+ */
+#define fsl_shw_sko_set_established_info(skobject, skuserid, skhandle) \
+{ \
+ (skobject)->userid = (skuserid); \
+ (skobject)->handle = (skhandle); \
+ (skobject)->flags |= FSL_SKO_KEY_ESTABLISHED; \
+ (skobject)->flags &= \
+ ~(FSL_SKO_KEY_PRESENT); \
+}
+
+/*!
+ * Retrieve the established-key handle from a key object.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skhandle The location to store the @a handle of the unwrapped
+ * key.
+ */
+#define fsl_shw_sko_get_established_info(skobject, skhandle) \
+ *(skhandle) = (skobject)->handle
+
+/*!
+ * Extract the algorithm from a key object.
+ *
+ * @param skobject The Key Object to be queried.
+ * @param[out] skalgorithm A pointer to the location to store the algorithm.
+ */
+#define fsl_shw_sko_get_algorithm(skobject, skalgorithm) \
+ *(skalgorithm) = (skobject)->algorithm
+
+/*!
+ * Retrieve the cleartext key from a key object that is stored in a user
+ * keystore.
+ *
+ * @param skobject The Key Object to be queried.
+ * @param[out] skkey A pointer to the location to store the key. NULL
+ * if the key is not stored in a user keystore.
+ */
+#define fsl_shw_sko_get_key(skobject, skkey) \
+{ \
+ fsl_shw_kso_t* keystore = (skobject)->keystore; \
+ if (keystore != NULL) { \
+ *(skkey) = keystore->slot_get_address(keystore->user_data, \
+ (skobject)->handle); \
+ } else { \
+ *(skkey) = NULL; \
+ } \
+}
+
+/*!
+ * Determine the size of a wrapped key based upon the cleartext key's length.
+ *
+ * This function can be used to calculate the number of octets that
+ * #fsl_shw_extract_key() will write into the location at @a covered_key.
+ *
+ * If zero is returned at @a length, this means that the key length in
+ * @a key_info is not supported.
+ *
+ * @param wkeyinfo Information about a key to be wrapped.
+ * @param wkeylen Location to store the length of a wrapped
+ * version of the key in @a key_info.
+ */
+#define fsl_shw_sko_calculate_wrapped_size(wkeyinfo, wkeylen) \
+{ \
+ register fsl_shw_sko_t* kp = wkeyinfo; \
+ register uint32_t kl = kp->key_length; \
+ int key_blocks = (kl + 15) / 16; \
+ int base_size = 35; /* ICV + T' + ALG + LEN + FLAGS */ \
+ \
+ *(wkeylen) = base_size + 16 * key_blocks; \
+}
+
+/*!
+ * Set some flags in the key object.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skflags (One or more) ORed members of #fsl_shw_key_flags_t which
+ * are to be set.
+ */
+#define fsl_shw_sko_set_flags(skobject, skflags) \
+ (skobject)->flags |= (skflags)
+
+/*!
+ * Clear some flags in the key object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skflags (One or more) ORed members of #fsl_shw_key_flags_t
+ * which are to be reset.
+ */
+#define fsl_shw_sko_clear_flags(skobject, skflags) \
+ (skobject)->flags &= ~(skflags)
+
+/*!
+ * Initialize a Hash Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the current message length and hash algorithm in the hash
+ * context object.
+ *
+ * @param hcobject The hash context to operate upon.
+ * @param hcalgorithm The hash algorithm to be used (#FSL_HASH_ALG_MD5,
+ * #FSL_HASH_ALG_SHA256, etc).
+ *
+ */
+#define fsl_shw_hco_init(hcobject, hcalgorithm) \
+{ \
+ (hcobject)->algorithm = hcalgorithm; \
+ (hcobject)->flags = 0; \
+ switch (hcalgorithm) { \
+ case FSL_HASH_ALG_MD5: \
+ (hcobject)->digest_length = 16; \
+ (hcobject)->context_length = 16; \
+ (hcobject)->context_register_length = 24; \
+ break; \
+ case FSL_HASH_ALG_SHA1: \
+ (hcobject)->digest_length = 20; \
+ (hcobject)->context_length = 20; \
+ (hcobject)->context_register_length = 24; \
+ break; \
+ case FSL_HASH_ALG_SHA224: \
+ (hcobject)->digest_length = 28; \
+ (hcobject)->context_length = 32; \
+ (hcobject)->context_register_length = 36; \
+ break; \
+ case FSL_HASH_ALG_SHA256: \
+ (hcobject)->digest_length = 32; \
+ (hcobject)->context_length = 32; \
+ (hcobject)->context_register_length = 36; \
+ break; \
+ default: \
+ /* error ! */ \
+ (hcobject)->digest_length = 1; \
+ (hcobject)->context_length = 1; \
+ (hcobject)->context_register_length = 1; \
+ break; \
+ } \
+}
+
+/*!
+ * Get the current hash value and message length from the hash context object.
+ *
+ * The algorithm must have already been specified. See #fsl_shw_hco_init().
+ *
+ * @param hcobject The hash context to query.
+ * @param[out] hccontext Pointer to the location of @a length octets where to
+ * store a copy of the current value of the digest.
+ * @param hcclength Number of octets of hash value to copy.
+ * @param[out] hcmsglen Pointer to the location to store the number of octets
+ * already hashed.
+ */
+#define fsl_shw_hco_get_digest(hcobject, hccontext, hcclength, hcmsglen) \
+{ \
+ copy_bytes(hccontext, (hcobject)->context, hcclength); \
+ if ((hcobject)->algorithm == FSL_HASH_ALG_SHA224 \
+ || (hcobject)->algorithm == FSL_HASH_ALG_SHA256) { \
+ *(hcmsglen) = (hcobject)->context[8]; \
+ } else { \
+ *(hcmsglen) = (hcobject)->context[5]; \
+ } \
+}
+
+/*!
+ * Get the hash algorithm from the hash context object.
+ *
+ * @param hcobject The hash context to query.
+ * @param[out] hcalgorithm Pointer to where the algorithm is to be stored.
+ */
+#define fsl_shw_hco_get_info(hcobject, hcalgorithm) \
+{ \
+ *(hcalgorithm) = (hcobject)->algorithm; \
+}
+
+/*!
+ * Set the current hash value and message length in the hash context object.
+ *
+ * The algorithm must have already been specified. See #fsl_shw_hco_init().
+ *
+ * @param hcobject The hash context to operate upon.
+ * @param hccontext Pointer to buffer of appropriate length to copy into
+ * the hash context object.
+ * @param hcmsglen The number of octets of the message which have
+ * already been hashed.
+ *
+ */
+#define fsl_shw_hco_set_digest(hcobject, hccontext, hcmsglen) \
+{ \
+ copy_bytes((hcobject)->context, hccontext, (hcobject)->context_length); \
+ if (((hcobject)->algorithm == FSL_HASH_ALG_SHA224) \
+ || ((hcobject)->algorithm == FSL_HASH_ALG_SHA256)) { \
+ (hcobject)->context[8] = hcmsglen; \
+ } else { \
+ (hcobject)->context[5] = hcmsglen; \
+ } \
+}
+
+/*!
+ * Set flags in a Hash Context Object.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hcobject The hash context to be operated on.
+ * @param hcflags The flags to be set in the context. These can be ORed
+ * members of #fsl_shw_hash_ctx_flags_t.
+ */
+#define fsl_shw_hco_set_flags(hcobject, hcflags) \
+ (hcobject)->flags |= (hcflags)
+
+/*!
+ * Clear flags in a Hash Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hcobject The hash context to be operated on.
+ * @param hcflags The flags to be reset in the context. These can be ORed
+ * members of #fsl_shw_hash_ctx_flags_t.
+ */
+#define fsl_shw_hco_clear_flags(hcobject, hcflags) \
+ (hcobject)->flags &= ~(hcflags)
+
+/*!
+ * Initialize an HMAC Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the current message length and hash algorithm in the HMAC
+ * context object.
+ *
+ * @param hcobject The HMAC context to operate upon.
+ * @param hcalgorithm The hash algorithm to be used (#FSL_HASH_ALG_MD5,
+ * #FSL_HASH_ALG_SHA256, etc).
+ *
+ */
+#define fsl_shw_hmco_init(hcobject, hcalgorithm) \
+ fsl_shw_hco_init(hcobject, hcalgorithm)
+
+/*!
+ * Set flags in an HMAC Context Object.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hcobject The HMAC context to be operated on.
+ * @param hcflags The flags to be set in the context. These can be ORed
+ * members of #fsl_shw_hmac_ctx_flags_t.
+ */
+#define fsl_shw_hmco_set_flags(hcobject, hcflags) \
+ (hcobject)->flags |= (hcflags)
+
+/*!
+ * Clear flags in an HMAC Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hcobject The HMAC context to be operated on.
+ * @param hcflags The flags to be reset in the context. These can be ORed
+ * members of #fsl_shw_hmac_ctx_flags_t.
+ */
+#define fsl_shw_hmco_clear_flags(hcobject, hcflags) \
+ (hcobject)->flags &= ~(hcflags)
+
+/*!
+ * Initialize a Symmetric Cipher Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. This will set the @a mode and @a algorithm and initialize the
+ * Object.
+ *
+ * @param scobject The context object to operate on.
+ * @param scalg The cipher algorithm this context will be used with.
+ * @param scmode #FSL_SYM_MODE_CBC, #FSL_SYM_MODE_ECB, etc.
+ *
+ */
+#define fsl_shw_scco_init(scobject, scalg, scmode) \
+{ \
+ register uint32_t bsb; /* block-size bytes */ \
+ \
+ switch (scalg) { \
+ case FSL_KEY_ALG_AES: \
+ bsb = 16; \
+ break; \
+ case FSL_KEY_ALG_DES: \
+ /* fall through */ \
+ case FSL_KEY_ALG_TDES: \
+ bsb = 8; \
+ break; \
+ case FSL_KEY_ALG_ARC4: \
+ bsb = 259; \
+ break; \
+ case FSL_KEY_ALG_HMAC: \
+ bsb = 1; /* meaningless */ \
+ break; \
+ default: \
+ bsb = 00; \
+ } \
+ (scobject)->block_size_bytes = bsb; \
+ (scobject)->mode = scmode; \
+ (scobject)->flags = 0; \
+}
+
+/*!
+ * Set the flags for a Symmetric Cipher Context.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param scobject The context object to operate on.
+ * @param scflags The flags to reset (one or more values from
+ * #fsl_shw_sym_ctx_flags_t ORed together).
+ *
+ */
+#define fsl_shw_scco_set_flags(scobject, scflags) \
+ (scobject)->flags |= (scflags)
+
+/*!
+ * Clear some flags in a Symmetric Cipher Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param scobject The context object to operate on.
+ * @param scflags The flags to reset (one or more values from
+ * #fsl_shw_sym_ctx_flags_t ORed together).
+ *
+ */
+#define fsl_shw_scco_clear_flags(scobject, scflags) \
+ (scobject)->flags &= ~(scflags)
+
+/*!
+ * Set the Context (IV) for a Symmetric Cipher Context.
+ *
+ * This is to set the context/IV for #FSL_SYM_MODE_CBC mode, or to set the
+ * context (the S-Box and pointers) for ARC4. The full context size will
+ * be copied.
+ *
+ * @param scobject The context object to operate on.
+ * @param sccontext A pointer to the buffer which contains the context.
+ *
+ */
+#define fsl_shw_scco_set_context(scobject, sccontext) \
+ copy_bytes((scobject)->context, sccontext, \
+ (scobject)->block_size_bytes)
+
+/*!
+ * Get the Context for a Symmetric Cipher Context.
+ *
+ * This is to retrieve the context/IV for #FSL_SYM_MODE_CBC mode, or to
+ * retrieve context (the S-Box and pointers) for ARC4. The full context
+ * will be copied.
+ *
+ * @param scobject The context object to operate on.
+ * @param[out] sccontext Pointer to location where context will be stored.
+ */
+#define fsl_shw_scco_get_context(scobject, sccontext) \
+ copy_bytes(sccontext, (scobject)->context, (scobject)->block_size_bytes)
+
+/*!
+ * Set the Counter Value for a Symmetric Cipher Context.
+ *
+ * This will set the Counter Value for CTR mode.
+ *
+ * @param scobject The context object to operate on.
+ * @param sccounter The starting counter value. The number of octets.
+ * copied will be the block size for the algorithm.
+ * @param scmodulus The modulus for controlling the incrementing of the
+ * counter.
+ *
+ */
+#define fsl_shw_scco_set_counter_info(scobject, sccounter, scmodulus) \
+ { \
+ if ((sccounter) != NULL) { \
+ copy_bytes((scobject)->context, sccounter, \
+ (scobject)->block_size_bytes); \
+ } \
+ (scobject)->modulus_exp = scmodulus; \
+ }
+
+/*!
+ * Get the Counter Value for a Symmetric Cipher Context.
+ *
+ * This will retrieve the Counter Value is for CTR mode.
+ *
+ * @param scobject The context object to query.
+ * @param[out] sccounter Pointer to location to store the current counter
+ * value. The number of octets copied will be the
+ * block size for the algorithm.
+ * @param[out] scmodulus Pointer to location to store the modulus.
+ *
+ */
+#define fsl_shw_scco_get_counter_info(scobject, sccounter, scmodulus) \
+ { \
+ if ((sccounter) != NULL) { \
+ copy_bytes(sccounter, (scobject)->context, \
+ (scobject)->block_size_bytes); \
+ } \
+ if ((scmodulus) != NULL) { \
+ *(scmodulus) = (scobject)->modulus_exp; \
+ } \
+ }
+
+/*!
+ * Initialize a Authentication-Cipher Context.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acmode The mode for this object (only #FSL_ACC_MODE_CCM
+ * supported).
+ */
+#define fsl_shw_acco_init(acobject, acmode) \
+ { \
+ (acobject)->flags = 0; \
+ (acobject)->mode = (acmode); \
+ }
+
+/*!
+ * Set the flags for a Authentication-Cipher Context.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acflags The flags to set (one or more from
+ * #fsl_shw_auth_ctx_flags_t ORed together).
+ *
+ */
+#define fsl_shw_acco_set_flags(acobject, acflags) \
+ (acobject)->flags |= (acflags)
+
+/*!
+ * Clear some flags in a Authentication-Cipher Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acflags The flags to reset (one or more from
+ * #fsl_shw_auth_ctx_flags_t ORed together).
+ *
+ */
+#define fsl_shw_acco_clear_flags(acobject, acflags) \
+ (acobject)->flags &= ~(acflags)
+
+/*!
+ * Set up the Authentication-Cipher Object for CCM mode.
+ *
+ * This will set the @a auth_object for CCM mode and save the @a ctr,
+ * and @a mac_length. This function can be called instead of
+ * #fsl_shw_acco_init().
+ *
+ * The paramater @a ctr is Counter Block 0, (counter value 0), which is for the
+ * MAC.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acalg Cipher algorithm. Only AES is supported.
+ * @param accounter The initial counter value.
+ * @param acmaclen The number of octets used for the MAC. Valid values are
+ * 4, 6, 8, 10, 12, 14, and 16.
+ */
+/* Do we need to stash the +1 value of the CTR somewhere? */
+#define fsl_shw_acco_set_ccm(acobject, acalg, accounter, acmaclen) \
+{ \
+ (acobject)->flags = 0; \
+ (acobject)->mode = FSL_ACC_MODE_CCM; \
+ (acobject)->auth_info.CCM_ctx_info.block_size_bytes = 16; \
+ (acobject)->cipher_ctx_info.block_size_bytes = 16; \
+ (acobject)->mac_length = acmaclen; \
+ fsl_shw_scco_set_counter_info(&(acobject)->cipher_ctx_info, accounter, \
+ FSL_CTR_MOD_128); \
+}
+
+/*!
+ * Format the First Block (IV) & Initial Counter Value per NIST CCM.
+ *
+ * This function will also set the IV and CTR values per Appendix A of NIST
+ * Special Publication 800-38C (May 2004). It will also perform the
+ * #fsl_shw_acco_set_ccm() operation with information derived from this set of
+ * parameters.
+ *
+ * Note this function assumes the algorithm is AES. It initializes the
+ * @a auth_object by setting the mode to #FSL_ACC_MODE_CCM and setting the
+ * flags to be #FSL_ACCO_NIST_CCM.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param act The number of octets used for the MAC. Valid values are
+ * 4, 6, 8, 10, 12, 14, and 16.
+ * @param acad Number of octets of Associated Data (may be zero).
+ * @param acq A value for the size of the length of @a q field. Valid
+ * values are 1-8.
+ * @param acN The Nonce (packet number or other changing value). Must
+ * be (15 - @a q_length) octets long.
+ * @param acQ The value of Q (size of the payload in octets).
+ *
+ */
+/* Do we need to stash the +1 value of the CTR somewhere? */
+#define fsl_shw_ccm_nist_format_ctr_and_iv(acobject, act, acad, acq, acN, acQ)\
+ { \
+ uint64_t Q = acQ; \
+ uint8_t bflag = ((acad)?0x40:0) | ((((act)-2)/2)<<3) | ((acq)-1); \
+ unsigned i; \
+ uint8_t* qptr = (acobject)->auth_info.CCM_ctx_info.context + 15; \
+ (acobject)->auth_info.CCM_ctx_info.block_size_bytes = 16; \
+ (acobject)->cipher_ctx_info.block_size_bytes = 16; \
+ (acobject)->mode = FSL_ACC_MODE_CCM; \
+ (acobject)->flags = FSL_ACCO_NIST_CCM; \
+ \
+ /* Store away the MAC length (after calculating actual value */ \
+ (acobject)->mac_length = (act); \
+ /* Set Flag field in Block 0 */ \
+ *((acobject)->auth_info.CCM_ctx_info.context) = bflag; \
+ /* Set Nonce field in Block 0 */ \
+ copy_bytes((acobject)->auth_info.CCM_ctx_info.context+1, acN, \
+ 15-(acq)); \
+ /* Set Flag field in ctr */ \
+ *((acobject)->cipher_ctx_info.context) = (acq)-1; \
+ /* Update the Q (payload length) field of Block0 */ \
+ (acobject)->q_length = acq; \
+ for (i = 0; i < (acq); i++) { \
+ *qptr-- = Q & 0xFF; \
+ Q >>= 8; \
+ } \
+ /* Set the Nonce field of the ctr */ \
+ copy_bytes((acobject)->cipher_ctx_info.context+1, acN, 15-(acq)); \
+ /* Clear the block counter field of the ctr */ \
+ memset((acobject)->cipher_ctx_info.context+16-(acq), 0, (acq)+1); \
+ }
+
+/*!
+ * Update the First Block (IV) & Initial Counter Value per NIST CCM.
+ *
+ * This function will set the IV and CTR values per Appendix A of NIST Special
+ * Publication 800-38C (May 2004).
+ *
+ * Note this function assumes that #fsl_shw_ccm_nist_format_ctr_and_iv() has
+ * previously been called on the @a auth_object.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acN The Nonce (packet number or other changing value). Must
+ * be (15 - @a q_length) octets long.
+ * @param acQ The value of Q (size of the payload in octets).
+ *
+ */
+/* Do we need to stash the +1 value of the CTR somewhere? */
+#define fsl_shw_ccm_nist_update_ctr_and_iv(acobject, acN, acQ) \
+ { \
+ uint64_t Q = acQ; \
+ unsigned i; \
+ uint8_t* qptr = (acobject)->auth_info.CCM_ctx_info.context + 15; \
+ \
+ /* Update the Nonce field field of Block0 */ \
+ copy_bytes((acobject)->auth_info.CCM_ctx_info.context+1, acN, \
+ 15 - (acobject)->q_length); \
+ /* Update the Q (payload length) field of Block0 */ \
+ for (i = 0; i < (acobject)->q_length; i++) { \
+ *qptr-- = Q & 0xFF; \
+ Q >>= 8; \
+ } \
+ /* Update the Nonce field of the ctr */ \
+ copy_bytes((acobject)->cipher_ctx_info.context+1, acN, \
+ 15 - (acobject)->q_length); \
+ }
+
+/******************************************************************************
+ * Library functions
+ *****************************************************************************/
+/* REQ-S2LRD-PINTFC-API-GEN-003 */
+extern fsl_shw_pco_t *fsl_shw_get_capabilities(fsl_shw_uco_t * user_ctx);
+
+/* REQ-S2LRD-PINTFC-API-GEN-004 */
+extern fsl_shw_return_t fsl_shw_register_user(fsl_shw_uco_t * user_ctx);
+
+/* REQ-S2LRD-PINTFC-API-GEN-005 */
+extern fsl_shw_return_t fsl_shw_deregister_user(fsl_shw_uco_t * user_ctx);
+
+/* REQ-S2LRD-PINTFC-API-GEN-006 */
+extern fsl_shw_return_t fsl_shw_get_results(fsl_shw_uco_t * user_ctx,
+ unsigned result_size,
+ fsl_shw_result_t results[],
+ unsigned *result_count);
+
+extern fsl_shw_return_t fsl_shw_establish_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_key_wrap_t establish_type,
+ const uint8_t * key);
+
+extern fsl_shw_return_t fsl_shw_extract_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ uint8_t * covered_key);
+
+extern fsl_shw_return_t fsl_shw_release_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info);
+
+extern void *fsl_shw_smalloc(fsl_shw_uco_t * user_ctx,
+ uint32_t size,
+ const uint8_t * UMID, uint32_t permissions);
+
+extern fsl_shw_return_t fsl_shw_sfree(fsl_shw_uco_t * user_ctx, void *address);
+
+extern fsl_shw_return_t fsl_shw_sstatus(fsl_shw_uco_t * user_ctx,
+ void *address,
+ fsl_shw_partition_status_t * status);
+
+extern fsl_shw_return_t fsl_shw_diminish_perms(fsl_shw_uco_t * user_ctx,
+ void *address,
+ uint32_t permissions);
+
+extern fsl_shw_return_t do_scc_engage_partition(fsl_shw_uco_t * user_ctx,
+ void *address,
+ const uint8_t * UMID,
+ uint32_t permissions);
+
+extern fsl_shw_return_t do_system_keystore_slot_alloc(fsl_shw_uco_t * user_ctx,
+ uint32_t key_lenth,
+ uint64_t ownerid,
+ uint32_t * slot);
+
+extern fsl_shw_return_t do_system_keystore_slot_dealloc(fsl_shw_uco_t *
+ user_ctx,
+ uint64_t ownerid,
+ uint32_t slot);
+
+extern fsl_shw_return_t do_system_keystore_slot_load(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ const uint8_t * key,
+ uint32_t key_length);
+
+extern fsl_shw_return_t do_system_keystore_slot_read(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ const uint8_t * key);
+
+extern fsl_shw_return_t do_system_keystore_slot_encrypt(fsl_shw_uco_t *
+ user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ uint8_t * black_data);
+
+extern fsl_shw_return_t do_system_keystore_slot_decrypt(fsl_shw_uco_t *
+ user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ const uint8_t *
+ black_data);
+
+extern fsl_shw_return_t
+do_scc_encrypt_region(fsl_shw_uco_t * user_ctx,
+ void *partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, uint8_t * black_data,
+ uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode);
+
+extern fsl_shw_return_t
+do_scc_decrypt_region(fsl_shw_uco_t * user_ctx,
+ void *partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, const uint8_t * black_data,
+ uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode);
+
+extern fsl_shw_return_t
+system_keystore_get_slot_info(uint64_t owner_id, uint32_t slot,
+ uint32_t * address, uint32_t * slot_size_bytes);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-SYM-002 */
+/* PINTFC-API-BASIC-SYM-ARC4-001 */
+/* PINTFC-API-BASIC-SYM-ARC4-002 */
+extern fsl_shw_return_t fsl_shw_symmetric_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * pt,
+ uint8_t * ct);
+
+/* PINTFC-API-BASIC-SYM-002 */
+/* PINTFC-API-BASIC-SYM-ARC4-001 */
+/* PINTFC-API-BASIC-SYM-ARC4-002 */
+extern fsl_shw_return_t fsl_shw_symmetric_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * ct,
+ uint8_t * pt);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-005 */
+extern fsl_shw_return_t fsl_shw_hash(fsl_shw_uco_t * user_ctx,
+ fsl_shw_hco_t * hash_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HMAC-001 */
+extern fsl_shw_return_t fsl_shw_hmac_precompute(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HMAC-002 */
+extern fsl_shw_return_t fsl_shw_hmac(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-RNG-002 */
+extern fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data);
+
+extern fsl_shw_return_t fsl_shw_add_entropy(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data);
+
+extern fsl_shw_return_t fsl_shw_gen_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * cipher_key_info,
+ fsl_shw_sko_t * auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t * auth_data,
+ uint32_t payload_length,
+ const uint8_t * payload,
+ uint8_t * ct, uint8_t * auth_value);
+
+extern fsl_shw_return_t fsl_shw_auth_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * cipher_key_info,
+ fsl_shw_sko_t * auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t * auth_data,
+ uint32_t payload_length,
+ const uint8_t * ct,
+ const uint8_t * auth_value,
+ uint8_t * payload);
+
+extern fsl_shw_return_t fsl_shw_read_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ uint8_t * key);
+
+static inline fsl_shw_return_t fsl_shw_gen_random_pf_key(fsl_shw_uco_t *
+ user_ctx)
+{
+ (void)user_ctx;
+
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+static inline fsl_shw_return_t fsl_shw_read_tamper_event(fsl_shw_uco_t *
+ user_ctx,
+ fsl_shw_tamper_t *
+ tamperp,
+ uint64_t * timestampp)
+{
+ (void)user_ctx;
+ (void)tamperp;
+ (void)timestampp;
+
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t sah_Append_Desc(const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_head,
+ const uint32_t header,
+ sah_Link * link1, sah_Link * link2);
+
+/* Utility Function leftover from sahara1 API */
+void sah_Descriptor_Chain_Destroy(const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/* Utility Function leftover from sahara1 API */
+fsl_shw_return_t sah_Descriptor_Chain_Execute(sah_Head_Desc * desc_chain,
+ fsl_shw_uco_t * user_ctx);
+
+fsl_shw_return_t sah_Append_Link(const sah_Mem_Util * mu,
+ sah_Link * link,
+ uint8_t * p,
+ const size_t length,
+ const sah_Link_Flags flags);
+
+fsl_shw_return_t sah_Create_Link(const sah_Mem_Util * mu,
+ sah_Link ** link,
+ uint8_t * p,
+ const size_t length,
+ const sah_Link_Flags flags);
+
+fsl_shw_return_t sah_Create_Key_Link(const sah_Mem_Util * mu,
+ sah_Link ** link,
+ fsl_shw_sko_t * key_info);
+
+void sah_Destroy_Link(const sah_Mem_Util * mu, sah_Link * link);
+
+void sah_Postprocess_Results(fsl_shw_uco_t * user_ctx,
+ sah_results * result_info);
+
+#endif /* SAHARA2_API_H */
diff --git a/drivers/mxc/security/sahara2/include/sahara2_kernel.h b/drivers/mxc/security/sahara2/include/sahara2_kernel.h
new file mode 100644
index 000000000000..bd2fae8e684a
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sahara2_kernel.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#define DRIVER_NAME sahara2
+
+#define SAHARA_MAJOR_NODE 78
+
+#include "portable_os.h"
+
+#include "platform_abstractions.h"
+
+/* Forward-declare prototypes using signature macros */
+
+OS_DEV_ISR_DCL(sahara2_isr);
+
+OS_DEV_INIT_DCL(sahara2_init);
+
+OS_DEV_SHUTDOWN_DCL(sahara2_shutdown);
+
+OS_DEV_OPEN_DCL(sahara2_open);
+
+OS_DEV_CLOSE_DCL(sahara2_release);
+
+OS_DEV_IOCTL_DCL(sahara2_ioctl);
+
+struct sahara2_kernel_user {
+ void *command_ring[32];
+};
+
+struct sahara2_sym_arg {
+ char *key;
+ unsigned key_len;
+};
+
+/*! These need to be added to Linux / OS abstractions */
+/*
+module_init(OS_DEV_INIT_REF(sahara2_init));
+module_cleanup(OS_DEV_SHUTDOWN_REF(sahara2_shutdown));
+*/
diff --git a/drivers/mxc/security/sahara2/include/sf_util.h b/drivers/mxc/security/sahara2/include/sf_util.h
new file mode 100644
index 000000000000..f64c3f7c393c
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sf_util.h
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+* @file sf_util.h
+*
+* @brief Header for Sahara Descriptor-chain building Functions
+*/
+#ifndef SF_UTIL_H
+#define SF_UTIL_H
+
+#include <fsl_platform.h>
+#include <sahara.h>
+
+/*! Header value for Sahara Descriptor 1 */
+#define SAH_HDR_SKHA_SET_MODE_IV_KEY 0x10880000
+/*! Header value for Sahara Descriptor 2 */
+#define SAH_HDR_SKHA_SET_MODE_ENC_DEC 0x108D0000
+/*! Header value for Sahara Descriptor 4 */
+#define SAH_HDR_SKHA_ENC_DEC 0x90850000
+/*! Header value for Sahara Descriptor 5 */
+#define SAH_HDR_SKHA_READ_CONTEXT_IV 0x10820000
+/*! Header value for Sahara Descriptor 6 */
+#define SAH_HDR_MDHA_SET_MODE_MD_KEY 0x20880000
+/*! Header value for Sahara Descriptor 8 */
+#define SAH_HDR_MDHA_SET_MODE_HASH 0x208D0000
+/*! Header value for Sahara Descriptor 10 */
+#define SAH_HDR_MDHA_HASH 0xA0850000
+/*! Header value for Sahara Descriptor 11 */
+#define SAH_HDR_MDHA_STORE_DIGEST 0x20820000
+/*! Header value for Sahara Descriptor 18 */
+#define SAH_HDR_RNG_GENERATE 0x308C0000
+/*! Header value for Sahara Descriptor 19 */
+#define SAH_HDR_PKHA_LD_N_E 0xC0800000
+/*! Header value for Sahara Descriptor 20 */
+#define SAH_HDR_PKHA_LD_A_EX_ST_B 0x408D0000
+/*! Header value for Sahara Descriptor 21 */
+#define SAH_HDR_PKHA_LD_N_EX_ST_B 0x408E0000
+/*! Header value for Sahara Descriptor 22 */
+#define SAH_HDR_PKHA_LD_A_B 0xC0830000
+/*! Header value for Sahara Descriptor 23 */
+#define SAH_HDR_PKHA_LD_A0_A1 0x40840000
+/*! Header value for Sahara Descriptor 24 */
+#define SAH_HDR_PKHA_LD_A2_A3 0xC0850000
+/*! Header value for Sahara Descriptor 25 */
+#define SAH_HDR_PKHA_LD_B0_B1 0xC0860000
+/*! Header value for Sahara Descriptor 26 */
+#define SAH_HDR_PKHA_LD_B2_B3 0x40870000
+/*! Header value for Sahara Descriptor 27 */
+#define SAH_HDR_PKHA_ST_A_B 0x40820000
+/*! Header value for Sahara Descriptor 28 */
+#define SAH_HDR_PKHA_ST_A0_A1 0x40880000
+/*! Header value for Sahara Descriptor 29 */
+#define SAH_HDR_PKHA_ST_A2_A3 0xC0890000
+/*! Header value for Sahara Descriptor 30 */
+#define SAH_HDR_PKHA_ST_B0_B1 0xC08A0000
+/*! Header value for Sahara Descriptor 31 */
+#define SAH_HDR_PKHA_ST_B2_B3 0x408B0000
+/*! Header value for Sahara Descriptor 32 */
+#define SAH_HDR_PKHA_EX_ST_B1 0xC08C0000
+/*! Header value for Sahara Descriptor 33 */
+#define SAH_HDR_ARC4_SET_MODE_SBOX 0x90890000
+/*! Header value for Sahara Descriptor 34 */
+#define SAH_HDR_ARC4_READ_SBOX 0x90860000
+/*! Header value for Sahara Descriptor 35 */
+#define SAH_HDR_ARC4_SET_MODE_KEY 0x90830000
+/*! Header value for Sahara Descriptor 36 */
+#define SAH_HDR_PKHA_LD_A3_B0 0x40810000
+/*! Header value for Sahara Descriptor 37 */
+#define SAH_HDR_PKHA_ST_B1_B2 0xC08F0000
+/*! Header value for Sahara Descriptor 38 */
+#define SAH_HDR_SKHA_CBC_ICV 0x10840000
+/*! Header value for Sahara Descriptor 39 */
+#define SAH_HDR_MDHA_ICV_CHECK 0xA08A0000
+
+/*! Header bit indicating "Link-List optimization" */
+#define SAH_HDR_LLO 0x01000000
+
+#define SAH_SF_DCLS \
+ fsl_shw_return_t ret; \
+ unsigned sf_executed = 0; \
+ sah_Head_Desc* desc_chain = NULL; \
+ uint32_t header
+
+#define SAH_SF_USER_CHECK() \
+do { \
+ ret = sah_validate_uco(user_ctx); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+} while (0)
+
+#define SAH_SF_EXECUTE() \
+do { \
+ sf_executed = 1; \
+ ret = sah_Descriptor_Chain_Execute(desc_chain, user_ctx); \
+} while (0)
+
+#define SAH_SF_DESC_CLEAN() \
+do { \
+ if (!sf_executed || (user_ctx->flags & FSL_UCO_BLOCKING_MODE)) { \
+ sah_Descriptor_Chain_Destroy(user_ctx->mem_util, &desc_chain); \
+ } \
+ (void) header; \
+} while (0)
+
+/*! Add Descriptor with two inputs */
+#define DESC_IN_IN(hdr, len1, ptr1, len2, ptr2) \
+{ \
+ ret = sah_add_two_in_desc(hdr, ptr1, len1, ptr2, len2, \
+ user_ctx->mem_util, &desc_chain); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+}
+
+/*! Add Descriptor with two vectors */
+#define DESC_D_D(hdr, len1, ptr1, len2, ptr2) \
+{ \
+ ret = sah_add_two_d_desc(hdr, ptr1, len1, ptr2, len2, \
+ user_ctx->mem_util, &desc_chain); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+}
+
+/*! Add Descriptor with input and a key */
+#define DESC_IN_KEY(hdr, len1, ptr1, key2) \
+{ \
+ ret = sah_add_in_key_desc(hdr, ptr1, len1, key2, \
+ user_ctx->mem_util, &desc_chain); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+}
+
+/*! Add Descriptor with input and an output */
+#define DESC_IN_OUT(hdr, len1, ptr1, len2, ptr2) \
+{ \
+ ret = sah_add_in_out_desc(hdr, ptr1, len1, ptr2, len2, \
+ user_ctx->mem_util, &desc_chain); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+}
+
+/*! Add Descriptor with input and a key output */
+#define DESC_IN_KEYOUT(hdr, len1, ptr1, key2) \
+{ \
+ ret = sah_add_in_keyout_desc(hdr, ptr1, len1, key2, \
+ user_ctx->mem_util, &desc_chain); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+}
+
+/*! Add Descriptor with a key and an output */
+#define DESC_KEY_OUT(hdr, key1, len2, ptr2) \
+{ \
+ ret = sah_add_key_out_desc(hdr, key1, ptr2, len2, \
+ user_ctx->mem_util, &desc_chain); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+}
+
+/*! Add Descriptor with two outputs */
+#define DESC_OUT_OUT(hdr, len1, ptr1, len2, ptr2) \
+{ \
+ ret = sah_add_two_out_desc(hdr, ptr1, len1, ptr2, len2, \
+ user_ctx->mem_util, &desc_chain); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+}
+
+/*! Add Descriptor with output then input pointers */
+#define DESC_OUT_IN(hdr, len1, ptr1, len2, ptr2) \
+{ \
+ ret = sah_add_out_in_desc(hdr, ptr1, len1, ptr2, len2, \
+ user_ctx->mem_util, &desc_chain); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+}
+
+#ifdef SAH_SF_DEBUG
+/*! Add Descriptor with two outputs */
+#define DBG_DESC(hdr, len1, ptr1, len2, ptr2) \
+{ \
+ ret = sah_add_two_out_desc(hdr, ptr1, len1, ptr2, len2, \
+ user_ctx->mem_util, &desc_chain); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+}
+#else
+#define DBG_DESC(hdr, len1, ptr1, len2, ptr2)
+#endif
+
+#ifdef __KERNEL__
+#define DESC_DBG_ON ({console_loglevel = 8;})
+#define DESC_DBG_OFF ({console_loglevel = 7;})
+#else
+#define DESC_DBG_ON system("echo 8 > /proc/sys/kernel/printk")
+#define DESC_DBG_OFF system("echo 7 > /proc/sys/kernel/printk")
+#endif
+
+#define DESC_TEMP_ALLOC(size) \
+({ \
+ uint8_t* ptr; \
+ ptr = user_ctx->mem_util->mu_malloc(user_ctx->mem_util->mu_ref, \
+ size); \
+ if (ptr == NULL) { \
+ ret = FSL_RETURN_NO_RESOURCE_S; \
+ goto out; \
+ } \
+ \
+ ptr; \
+})
+
+#define DESC_TEMP_FREE(ptr) \
+({ \
+ if ((ptr != NULL) && \
+ (!sf_executed || (user_ctx->flags & FSL_UCO_BLOCKING_MODE))) { \
+ user_ctx->mem_util-> \
+ mu_free(user_ctx->mem_util->mu_ref, ptr); \
+ ptr = NULL; \
+ } \
+})
+
+/* Temporary implementation. This needs to be in internal/secure RAM */
+#define DESC_TEMP_SECURE_ALLOC(size) \
+({ \
+ uint8_t* ptr; \
+ ptr = user_ctx->mem_util->mu_malloc(user_ctx->mem_util->mu_ref, \
+ size); \
+ if (ptr == NULL) { \
+ ret = FSL_RETURN_NO_RESOURCE_S; \
+ goto out; \
+ } \
+ \
+ ptr; \
+})
+
+#define DESC_TEMP_SECURE_FREE(ptr, size) \
+({ \
+ if ((ptr != NULL) && \
+ (!sf_executed || (user_ctx->flags & FSL_UCO_BLOCKING_MODE))) { \
+ user_ctx->mem_util->mu_memset(user_ctx->mem_util->mu_ref, \
+ ptr, 0, size); \
+ \
+ user_ctx->mem_util-> \
+ mu_free(user_ctx->mem_util->mu_ref, ptr); \
+ ptr = NULL; \
+ } \
+})
+
+extern const uint32_t sah_insert_mdha_algorithm[];
+
+/*! @defgroup mdhaflags MDHA Mode Register Values
+ *
+ * These are bit fields and combinations of bit fields for setting the Mode
+ * Register portion of a Sahara Descriptor Header field.
+ *
+ * The parity bit has been set to ensure that these values have even parity,
+ * therefore using an Exclusive-OR operation against an existing header will
+ * preserve its parity.
+ *
+ * @addtogroup mdhaflags
+ @{
+ */
+#define sah_insert_mdha_icv_check 0x80001000
+#define sah_insert_mdha_ssl 0x80000400
+#define sah_insert_mdha_mac_full 0x80000200
+#define sah_insert_mdha_opad 0x80000080
+#define sah_insert_mdha_ipad 0x80000040
+#define sah_insert_mdha_init 0x80000020
+#define sah_insert_mdha_hmac 0x80000008
+#define sah_insert_mdha_pdata 0x80000004
+#define sah_insert_mdha_algorithm_sha224 0x00000003
+#define sah_insert_mdha_algorithm_sha256 0x80000002
+#define sah_insert_mdha_algorithm_md5 0x80000001
+#define sah_insert_mdha_algorithm_sha1 0x00000000
+/*! @} */
+
+extern const uint32_t sah_insert_skha_algorithm[];
+extern const uint32_t sah_insert_skha_mode[];
+extern const uint32_t sah_insert_skha_modulus[];
+
+/*! @defgroup skhaflags SKHA Mode Register Values
+ *
+ * These are bit fields and combinations of bit fields for setting the Mode
+ * Register portion of a Sahara Descriptor Header field.
+ *
+ * The parity bit has been set to ensure that these values have even parity,
+ * therefore using an Exclusive-OR operation against an existing header will
+ * preserve its parity.
+ *
+ * @addtogroup skhaflags
+ @{
+ */
+/*! */
+#define sah_insert_skha_modulus_128 0x00001e00
+#define sah_insert_skha_no_key_parity 0x80000100
+#define sah_insert_skha_ctr_last_block 0x80000020
+#define sah_insert_skha_suppress_cbc 0x80000020
+#define sah_insert_skha_no_permute 0x80000020
+#define sah_insert_skha_algorithm_arc4 0x00000003
+#define sah_insert_skha_algorithm_tdes 0x80000002
+#define sah_insert_skha_algorithm_des 0x80000001
+#define sah_insert_skha_algorithm_aes 0x00000000
+#define sah_insert_skha_aux0 0x80000020
+#define sah_insert_skha_mode_ctr 0x00000018
+#define sah_insert_skha_mode_ccm 0x80000010
+#define sah_insert_skha_mode_cbc 0x80000008
+#define sah_insert_skha_mode_ecb 0x00000000
+#define sah_insert_skha_encrypt 0x80000004
+#define sah_insert_skha_decrypt 0x00000000
+/*! @} */
+
+/*! @defgroup rngflags RNG Mode Register Values
+ *
+ */
+/*! */
+#define sah_insert_rng_gen_seed 0x80000001
+
+/*! @} */
+
+/*! @defgroup pkhaflags PKHA Mode Register Values
+ *
+ */
+/*! */
+#define sah_insert_pkha_soft_err_false 0x80000200
+#define sah_insert_pkha_soft_err_true 0x80000100
+
+#define sah_insert_pkha_rtn_clr_mem 0x80000001
+#define sah_insert_pkha_rtn_clr_eram 0x80000002
+#define sah_insert_pkha_rtn_mod_exp 0x00000003
+#define sah_insert_pkha_rtn_mod_r2modn 0x80000004
+#define sah_insert_pkha_rtn_mod_rrmodp 0x00000005
+#define sah_insert_pkha_rtn_ec_fp_aff_ptmul 0x00000006
+#define sah_insert_pkha_rtn_ec_f2m_aff_ptmul 0x80000007
+#define sah_insert_pkha_rtn_ec_fp_proj_ptmul 0x80000008
+#define sah_insert_pkha_rtn_ec_f2m_proj_ptmul 0x00000009
+#define sah_insert_pkha_rtn_ec_fp_add 0x0000000A
+#define sah_insert_pkha_rtn_ec_fp_double 0x8000000B
+#define sah_insert_pkha_rtn_ec_f2m_add 0x0000000C
+#define sah_insert_pkha_rtn_ec_f2m_double 0x8000000D
+#define sah_insert_pkha_rtn_f2m_r2modn 0x8000000E
+#define sah_insert_pkha_rtn_f2m_inv 0x0000000F
+#define sah_insert_pkha_rtn_mod_inv 0x80000010
+#define sah_insert_pkha_rtn_rsa_sstep 0x00000011
+#define sah_insert_pkha_rtn_mod_emodn 0x00000012
+#define sah_insert_pkha_rtn_f2m_emodn 0x80000013
+#define sah_insert_pkha_rtn_ec_fp_ptmul 0x00000014
+#define sah_insert_pkha_rtn_ec_f2m_ptmul 0x80000015
+#define sah_insert_pkha_rtn_f2m_gcd 0x80000016
+#define sah_insert_pkha_rtn_mod_gcd 0x00000017
+#define sah_insert_pkha_rtn_f2m_dbl_aff 0x00000018
+#define sah_insert_pkha_rtn_fp_dbl_aff 0x80000019
+#define sah_insert_pkha_rtn_f2m_add_aff 0x8000001A
+#define sah_insert_pkha_rtn_fp_add_aff 0x0000001B
+#define sah_insert_pkha_rtn_f2m_exp 0x8000001C
+#define sah_insert_pkha_rtn_mod_exp_teq 0x0000001D
+#define sah_insert_pkha_rtn_rsa_sstep_teq 0x0000001E
+#define sah_insert_pkha_rtn_f2m_multn 0x8000001F
+#define sah_insert_pkha_rtn_mod_multn 0x80000020
+#define sah_insert_pkha_rtn_mod_add 0x00000021
+#define sah_insert_pkha_rtn_mod_sub 0x00000022
+#define sah_insert_pkha_rtn_mod_mult1_mont 0x80000023
+#define sah_insert_pkha_rtn_mod_mult2_deconv 0x00000024
+#define sah_insert_pkha_rtn_f2m_add 0x80000025
+#define sah_insert_pkha_rtn_f2m_mult1_mont 0x80000026
+#define sah_insert_pkha_rtn_f2m_mult2_deconv 0x00000027
+#define sah_insert_pkha_rtn_miller_rabin 0x00000028
+#define sah_insert_pkha_rtn_mod_amodn 0x00000029
+#define sah_insert_pkha_rtn_f2m_amodn 0x8000002A
+/*! @} */
+
+/*! Add a descriptor with two input pointers */
+fsl_shw_return_t sah_add_two_in_desc(uint32_t header,
+ const uint8_t * in1,
+ uint32_t in1_length,
+ const uint8_t * in2,
+ uint32_t in2_length,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/*! Add a descriptor with two 'data' pointers */
+fsl_shw_return_t sah_add_two_d_desc(uint32_t header,
+ const uint8_t * in1,
+ uint32_t in1_length,
+ const uint8_t * in2,
+ uint32_t in2_length,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/*! Add a descriptor with an input and key pointer */
+fsl_shw_return_t sah_add_in_key_desc(uint32_t header,
+ const uint8_t * in1,
+ uint32_t in1_length,
+ fsl_shw_sko_t * key_info,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/*! Add a descriptor with two key pointers */
+fsl_shw_return_t sah_add_key_key_desc(uint32_t header,
+ fsl_shw_sko_t * key_info1,
+ fsl_shw_sko_t * key_info2,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/*! Add a descriptor with two output pointers */
+fsl_shw_return_t sah_add_two_out_desc(uint32_t header,
+ uint8_t * out1,
+ uint32_t out1_length,
+ uint8_t * out2,
+ uint32_t out2_length,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/*! Add a descriptor with an input and output pointer */
+fsl_shw_return_t sah_add_in_out_desc(uint32_t header,
+ const uint8_t * in, uint32_t in_length,
+ uint8_t * out, uint32_t out_length,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/*! Add a descriptor with an input and key output pointer */
+fsl_shw_return_t sah_add_in_keyout_desc(uint32_t header,
+ const uint8_t * in, uint32_t in_length,
+ fsl_shw_sko_t * key_info,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/*! Add a descriptor with a key and an output pointer */
+fsl_shw_return_t sah_add_key_out_desc(uint32_t header,
+ const fsl_shw_sko_t * key_info,
+ uint8_t * out, uint32_t out_length,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/*! Add a descriptor with an output and input pointer */
+fsl_shw_return_t sah_add_out_in_desc(uint32_t header,
+ uint8_t * out, uint32_t out_length,
+ const uint8_t * in, uint32_t in_length,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/*! Verify that supplied User Context Object is valid */
+fsl_shw_return_t sah_validate_uco(fsl_shw_uco_t * uco);
+
+#endif /* SF_UTIL_H */
+
+/* End of sf_util.h */
diff --git a/drivers/mxc/security/sahara2/km_adaptor.c b/drivers/mxc/security/sahara2/km_adaptor.c
new file mode 100644
index 000000000000..f2b7d564256d
--- /dev/null
+++ b/drivers/mxc/security/sahara2/km_adaptor.c
@@ -0,0 +1,849 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+* @file km_adaptor.c
+*
+* @brief The Adaptor component provides an interface to the
+* driver for a kernel user.
+*/
+
+#include <adaptor.h>
+#include <sf_util.h>
+#include <sah_queue_manager.h>
+#include <sah_memory_mapper.h>
+#include <fsl_shw_keystore.h>
+#ifdef FSL_HAVE_SCC
+#include <linux/mxc_scc_driver.h>
+#elif defined (FSL_HAVE_SCC2)
+#include <linux/mxc_scc2_driver.h>
+#endif
+
+
+EXPORT_SYMBOL(adaptor_Exec_Descriptor_Chain);
+EXPORT_SYMBOL(sah_register);
+EXPORT_SYMBOL(sah_deregister);
+EXPORT_SYMBOL(sah_get_results);
+EXPORT_SYMBOL(fsl_shw_smalloc);
+EXPORT_SYMBOL(fsl_shw_sfree);
+EXPORT_SYMBOL(fsl_shw_sstatus);
+EXPORT_SYMBOL(fsl_shw_diminish_perms);
+EXPORT_SYMBOL(do_scc_encrypt_region);
+EXPORT_SYMBOL(do_scc_decrypt_region);
+EXPORT_SYMBOL(do_system_keystore_slot_alloc);
+EXPORT_SYMBOL(do_system_keystore_slot_dealloc);
+EXPORT_SYMBOL(do_system_keystore_slot_load);
+EXPORT_SYMBOL(do_system_keystore_slot_read);
+EXPORT_SYMBOL(do_system_keystore_slot_encrypt);
+EXPORT_SYMBOL(do_system_keystore_slot_decrypt);
+
+
+#if defined(DIAG_DRV_IF) || defined(DIAG_MEM) || defined(DIAG_ADAPTOR)
+#include <diagnostic.h>
+#endif
+
+#if defined(DIAG_DRV_IF) || defined(DIAG_MEM) || defined(DIAG_ADAPTOR)
+#define MAX_DUMP 16
+
+#define DIAG_MSG_SIZE 300
+static char Diag_msg[DIAG_MSG_SIZE];
+#endif
+
+/* This is the wait queue to this mode of driver */
+DECLARE_WAIT_QUEUE_HEAD(Wait_queue_km);
+
+/*! This matches Sahara2 capabilities... */
+fsl_shw_pco_t sahara2_capabilities = {
+ 1, 3, /* api version number - major & minor */
+ 1, 6, /* driver version number - major & minor */
+ {
+ FSL_KEY_ALG_AES,
+ FSL_KEY_ALG_DES,
+ FSL_KEY_ALG_TDES,
+ FSL_KEY_ALG_ARC4},
+ {
+ FSL_SYM_MODE_STREAM,
+ FSL_SYM_MODE_ECB,
+ FSL_SYM_MODE_CBC,
+ FSL_SYM_MODE_CTR},
+ {
+ FSL_HASH_ALG_MD5,
+ FSL_HASH_ALG_SHA1,
+ FSL_HASH_ALG_SHA224,
+ FSL_HASH_ALG_SHA256},
+ /*
+ * The following table must be set to handle all values of key algorithm
+ * and sym mode, and be in the correct order..
+ */
+ { /* Stream, ECB, CBC, CTR */
+ {0, 0, 0, 0}, /* HMAC */
+ {0, 1, 1, 1}, /* AES */
+ {0, 1, 1, 0}, /* DES */
+ {0, 1, 1, 0}, /* 3DES */
+ {1, 0, 0, 0} /* ARC4 */
+ },
+ 0, 0,
+ 0, 0, 0,
+ {{0, 0}}
+};
+
+#ifdef DIAG_ADAPTOR
+void km_Dump_Chain(const sah_Desc * chain);
+
+void km_Dump_Region(const char *prefix, const unsigned char *data,
+ unsigned length);
+
+static void km_Dump_Link(const char *prefix, const sah_Link * link);
+
+void km_Dump_Words(const char *prefix, const unsigned *data, unsigned length);
+#endif
+
+/**** Memory routines ****/
+
+static void *my_malloc(void *ref, size_t n)
+{
+ register void *mem;
+
+#ifndef DIAG_MEM_ERRORS
+ mem = os_alloc_memory(n, GFP_KERNEL);
+
+#else
+ {
+ uint32_t rand;
+ /* are we feeling lucky ? */
+ os_get_random_bytes(&rand, sizeof(rand));
+ if ((rand % DIAG_MEM_CONST) == 0) {
+ mem = 0;
+ } else {
+ mem = os_alloc_memory(n, GFP_ATOMIC);
+ }
+ }
+#endif /* DIAG_MEM_ERRORS */
+
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "API kmalloc: %p for %d\n", mem, n);
+ LOG_KDIAG(Diag_msg);
+#endif
+ ref = 0; /* unused param warning */
+ return mem;
+}
+
+static sah_Head_Desc *my_alloc_head_desc(void *ref)
+{
+ register sah_Head_Desc *ptr;
+
+#ifndef DIAG_MEM_ERRORS
+ ptr = sah_Alloc_Head_Descriptor();
+
+#else
+ {
+ uint32_t rand;
+ /* are we feeling lucky ? */
+ os_get_random_bytes(&rand, sizeof(rand));
+ if ((rand % DIAG_MEM_CONST) == 0) {
+ ptr = 0;
+ } else {
+ ptr = sah_Alloc_Head_Descriptor();
+ }
+ }
+#endif
+ ref = 0;
+ return ptr;
+}
+
+static sah_Desc *my_alloc_desc(void *ref)
+{
+ register sah_Desc *ptr;
+
+#ifndef DIAG_MEM_ERRORS
+ ptr = sah_Alloc_Descriptor();
+
+#else
+ {
+ uint32_t rand;
+ /* are we feeling lucky ? */
+ os_get_random_bytes(&rand, sizeof(rand));
+ if ((rand % DIAG_MEM_CONST) == 0) {
+ ptr = 0;
+ } else {
+ ptr = sah_Alloc_Descriptor();
+ }
+ }
+#endif
+ ref = 0;
+ return ptr;
+}
+
+static sah_Link *my_alloc_link(void *ref)
+{
+ register sah_Link *ptr;
+
+#ifndef DIAG_MEM_ERRORS
+ ptr = sah_Alloc_Link();
+
+#else
+ {
+ uint32_t rand;
+ /* are we feeling lucky ? */
+ os_get_random_bytes(&rand, sizeof(rand));
+ if ((rand % DIAG_MEM_CONST) == 0) {
+ ptr = 0;
+ } else {
+ ptr = sah_Alloc_Link();
+ }
+ }
+#endif
+ ref = 0;
+ return ptr;
+}
+
+static void my_free(void *ref, void *ptr)
+{
+ ref = 0; /* unused param warning */
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "API kfree: %p\n", ptr);
+ LOG_KDIAG(Diag_msg);
+#endif
+ os_free_memory(ptr);
+}
+
+static void my_free_head_desc(void *ref, sah_Head_Desc * ptr)
+{
+ sah_Free_Head_Descriptor(ptr);
+}
+
+static void my_free_desc(void *ref, sah_Desc * ptr)
+{
+ sah_Free_Descriptor(ptr);
+}
+
+static void my_free_link(void *ref, sah_Link * ptr)
+{
+ sah_Free_Link(ptr);
+}
+
+static void *my_memcpy(void *ref, void *dest, const void *src, size_t n)
+{
+ ref = 0; /* unused param warning */
+ return memcpy(dest, src, n);
+}
+
+static void *my_memset(void *ref, void *ptr, int ch, size_t n)
+{
+ ref = 0; /* unused param warning */
+ return memset(ptr, ch, n);
+}
+
+/*! Standard memory manipulation routines for kernel API. */
+static sah_Mem_Util std_kernelmode_mem_util = {
+ .mu_ref = 0,
+ .mu_malloc = my_malloc,
+ .mu_alloc_head_desc = my_alloc_head_desc,
+ .mu_alloc_desc = my_alloc_desc,
+ .mu_alloc_link = my_alloc_link,
+ .mu_free = my_free,
+ .mu_free_head_desc = my_free_head_desc,
+ .mu_free_desc = my_free_desc,
+ .mu_free_link = my_free_link,
+ .mu_memcpy = my_memcpy,
+ .mu_memset = my_memset
+};
+
+fsl_shw_return_t get_capabilities(fsl_shw_uco_t * user_ctx,
+ fsl_shw_pco_t * capabilities)
+{
+ scc_config_t *scc_capabilities;
+
+ /* Fill in the Sahara2 capabilities. */
+ memcpy(capabilities, &sahara2_capabilities, sizeof(fsl_shw_pco_t));
+
+ /* Fill in the SCC portion of the capabilities object */
+ scc_capabilities = scc_get_configuration();
+ capabilities->scc_driver_major = scc_capabilities->driver_major_version;
+ capabilities->scc_driver_minor = scc_capabilities->driver_minor_version;
+ capabilities->scm_version = scc_capabilities->scm_version;
+ capabilities->smn_version = scc_capabilities->smn_version;
+ capabilities->block_size_bytes = scc_capabilities->block_size_bytes;
+
+#ifdef FSL_HAVE_SCC
+ capabilities->scc_info.black_ram_size_blocks =
+ scc_capabilities->black_ram_size_blocks;
+ capabilities->scc_info.red_ram_size_blocks =
+ scc_capabilities->red_ram_size_blocks;
+#elif defined(FSL_HAVE_SCC2)
+ capabilities->scc2_info.partition_size_bytes =
+ scc_capabilities->partition_size_bytes;
+ capabilities->scc2_info.partition_count =
+ scc_capabilities->partition_count;
+#endif
+
+ return FSL_RETURN_OK_S;
+}
+
+/*!
+ * Sends a request to register this user
+ *
+ * @brief Sends a request to register this user
+ *
+ * @param[in,out] user_ctx part of the structure contains input parameters and
+ * part is filled in by the driver
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_register(fsl_shw_uco_t * user_ctx)
+{
+ fsl_shw_return_t status;
+
+ /* this field is used in user mode to indicate a file open has occured.
+ * it is used here, in kernel mode, to indicate that the uco is registered
+ */
+ user_ctx->sahara_openfd = 0; /* set to 'registered' */
+ user_ctx->mem_util = &std_kernelmode_mem_util;
+
+ /* check that uco is valid */
+ status = sah_validate_uco(user_ctx);
+
+ /* If life is good, register this user */
+ if (status == FSL_RETURN_OK_S) {
+ status = sah_handle_registration(user_ctx);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ user_ctx->sahara_openfd = -1; /* set to 'not registered' */
+ }
+
+ return status;
+}
+
+/*!
+ * Sends a request to deregister this user
+ *
+ * @brief Sends a request to deregister this user
+ *
+ * @param[in,out] user_ctx Info on user being deregistered.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_deregister(fsl_shw_uco_t * user_ctx)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+
+ if (user_ctx->sahara_openfd == 0) {
+ status = sah_handle_deregistration(user_ctx);
+ user_ctx->sahara_openfd = -1; /* set to 'no registered */
+ }
+
+ return status;
+}
+
+/*!
+ * Sends a request to get results for this user
+ *
+ * @brief Sends a request to get results for this user
+ *
+ * @param[in,out] arg Pointer to structure to collect results
+ * @param uco User's context
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_get_results(sah_results * arg, fsl_shw_uco_t * uco)
+{
+ fsl_shw_return_t code = sah_get_results_from_pool(uco, arg);
+
+ if ((code == FSL_RETURN_OK_S) && (arg->actual != 0)) {
+ sah_Postprocess_Results(uco, arg);
+ }
+
+ return code;
+}
+
+/*!
+ * This function writes the Descriptor Chain to the kernel driver.
+ *
+ * @brief Writes the Descriptor Chain to the kernel driver.
+ *
+ * @param dar A pointer to a Descriptor Chain of type sah_Head_Desc
+ * @param uco The user context object
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t adaptor_Exec_Descriptor_Chain(sah_Head_Desc * dar,
+ fsl_shw_uco_t * uco)
+{
+ sah_Head_Desc *kernel_space_desc = NULL;
+ fsl_shw_return_t code = FSL_RETURN_OK_S;
+ int os_error_code = 0;
+ unsigned blocking_mode = dar->uco_flags & FSL_UCO_BLOCKING_MODE;
+
+#ifdef DIAG_ADAPTOR
+ km_Dump_Chain(&dar->desc);
+#endif
+
+ dar->user_info = uco;
+ dar->user_desc = dar;
+
+ /* This code has been shamelessly copied from sah_driver_interface.c */
+ /* It needs to be moved somewhere common ... */
+ kernel_space_desc = sah_Physicalise_Descriptors(dar);
+
+ if (kernel_space_desc == NULL) {
+ /* We may have failed due to a -EFAULT as well, but we will return
+ * -ENOMEM since either way it is a memory related failure. */
+ code = FSL_RETURN_NO_RESOURCE_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("sah_Physicalise_Descriptors() failed\n");
+#endif
+ } else {
+ if (blocking_mode) {
+#ifdef SAHARA_POLL_MODE
+ os_error_code = sah_Handle_Poll(dar);
+#else
+ os_error_code = sah_blocking_mode(dar);
+#endif
+ if (os_error_code != 0) {
+ code = FSL_RETURN_ERROR_S;
+ } else { /* status of actual operation */
+ code = dar->result;
+ }
+ } else {
+#ifdef SAHARA_POLL_MODE
+ sah_Handle_Poll(dar);
+#else
+ /* just put someting in the DAR */
+ sah_Queue_Manager_Append_Entry(dar);
+#endif /* SAHARA_POLL_MODE */
+ }
+ }
+
+ return code;
+}
+
+
+/* System keystore context, defined in sah_driver_interface.c */
+extern fsl_shw_kso_t system_keystore;
+
+fsl_shw_return_t do_system_keystore_slot_alloc(fsl_shw_uco_t * user_ctx,
+ uint32_t key_length,
+ uint64_t ownerid,
+ uint32_t * slot)
+{
+ (void)user_ctx;
+ return keystore_slot_alloc(&system_keystore, key_length, ownerid, slot);
+}
+
+fsl_shw_return_t do_system_keystore_slot_dealloc(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot)
+{
+ (void)user_ctx;
+ return keystore_slot_dealloc(&system_keystore, ownerid, slot);
+}
+
+fsl_shw_return_t do_system_keystore_slot_load(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ const uint8_t * key,
+ uint32_t key_length)
+{
+ (void)user_ctx;
+ return keystore_slot_load(&system_keystore, ownerid, slot,
+ (void *)key, key_length);
+}
+
+fsl_shw_return_t do_system_keystore_slot_read(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ const uint8_t * key)
+{
+ (void)user_ctx;
+ return keystore_slot_read(&system_keystore, ownerid, slot,
+ key_length, (void *)key);
+}
+
+fsl_shw_return_t do_system_keystore_slot_encrypt(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ uint8_t * black_data)
+{
+ (void)user_ctx;
+ return keystore_slot_encrypt(NULL, &system_keystore, ownerid,
+ slot, key_length, black_data);
+}
+
+fsl_shw_return_t do_system_keystore_slot_decrypt(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ const uint8_t * black_data)
+{
+ (void)user_ctx;
+ return keystore_slot_decrypt(NULL, &system_keystore, ownerid,
+ slot, key_length, black_data);
+}
+
+void *fsl_shw_smalloc(fsl_shw_uco_t * user_ctx,
+ uint32_t size, const uint8_t * UMID, uint32_t permissions)
+{
+#ifdef FSL_HAVE_SCC2
+ int part_no;
+ void *part_base;
+ uint32_t part_phys;
+ scc_config_t *scc_configuration;
+
+ /* Check that the memory size requested is correct */
+ scc_configuration = scc_get_configuration();
+ if (size != scc_configuration->partition_size_bytes) {
+ return NULL;
+ }
+
+ /* Attempt to grab a partition. */
+ if (scc_allocate_partition(0, &part_no, &part_base, &part_phys)
+ != SCC_RET_OK) {
+ return NULL;
+ }
+ printk(KERN_ALERT "In fsh_shw_smalloc (km): partition_base:%p "
+ "partition_base_phys: %p\n", part_base, (void *)part_phys);
+
+ /* these bits should be in a separate function */
+ printk(KERN_ALERT "writing UMID and MAP to secure the partition\n");
+
+ scc_engage_partition(part_base, UMID, permissions);
+
+ (void)user_ctx; /* unused param warning */
+
+ return part_base;
+#else /* FSL_HAVE_SCC2 */
+ (void)user_ctx;
+ (void)size;
+ (void)UMID;
+ (void)permissions;
+ return NULL;
+#endif /* FSL_HAVE_SCC2 */
+
+}
+
+fsl_shw_return_t fsl_shw_sfree(fsl_shw_uco_t * user_ctx, void *address)
+{
+ (void)user_ctx;
+
+#ifdef FSL_HAVE_SCC2
+ if (scc_release_partition(address) == SCC_RET_OK) {
+ return FSL_RETURN_OK_S;
+ }
+#endif
+
+ return FSL_RETURN_ERROR_S;
+}
+
+fsl_shw_return_t fsl_shw_sstatus(fsl_shw_uco_t * user_ctx,
+ void *address,
+ fsl_shw_partition_status_t * status)
+{
+ (void)user_ctx;
+
+#ifdef FSL_HAVE_SCC2
+ *status = scc_partition_status(address);
+ return FSL_RETURN_OK_S;
+#endif
+
+ return FSL_RETURN_ERROR_S;
+}
+
+/* Diminish permissions on some secure memory */
+fsl_shw_return_t fsl_shw_diminish_perms(fsl_shw_uco_t * user_ctx,
+ void *address, uint32_t permissions)
+{
+
+ (void)user_ctx; /* unused parameter warning */
+
+#ifdef FSL_HAVE_SCC2
+ if (scc_diminish_permissions(address, permissions) == SCC_RET_OK) {
+ return FSL_RETURN_OK_S;
+ }
+#endif
+ return FSL_RETURN_ERROR_S;
+}
+
+/*
+ * partition_base - physical address of the partition
+ * offset - offset, in blocks, of the data from the start of the partition
+ * length - length, in bytes, of the data to be encrypted (multiple of 4)
+ * black_data - virtual address that the encrypted data should be stored at
+ * Note that this virtual address must be translatable using the __virt_to_phys
+ * macro; ie, it can't be a specially mapped address. To do encryption with those
+ * addresses, use the scc_encrypt_region function directly. This is to make
+ * this function compatible with the user mode declaration, which does not know
+ * the physical addresses of the data it is using.
+ */
+fsl_shw_return_t
+do_scc_encrypt_region(fsl_shw_uco_t * user_ctx,
+ void *partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, uint8_t * black_data,
+ uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode)
+{
+ scc_return_t scc_ret;
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+
+#ifdef FSL_HAVE_SCC2
+
+#ifdef DIAG_ADAPTOR
+ uint32_t *owner_32 = (uint32_t *) & (owner_id);
+
+ LOG_KDIAG_ARGS
+ ("partition base: %p, offset: %i, count: %i, black data: %p\n",
+ partition_base, offset_bytes, byte_count, (void *)black_data);
+#endif
+ (void)user_ctx;
+
+ os_cache_flush_range(black_data, byte_count);
+
+ scc_ret =
+ scc_encrypt_region((uint32_t) partition_base, offset_bytes,
+ byte_count, __virt_to_phys(black_data), IV,
+ cypher_mode);
+
+ if (scc_ret == SCC_RET_OK) {
+ retval = FSL_RETURN_OK_S;
+ } else {
+ retval = FSL_RETURN_ERROR_S;
+ }
+
+ /* The SCC2 DMA engine should have written to the black ram, so we need to
+ * invalidate that region of memory. Note that the red ram is not an
+ * because it is mapped with the cache disabled.
+ */
+ os_cache_inv_range(black_data, byte_count);
+
+#else
+ (void)scc_ret;
+#endif /* FSL_HAVE_SCC2 */
+
+ return retval;
+}
+
+/*!
+ * Call the proper function to decrypt a region of encrypted secure memory
+ *
+ * @brief
+ *
+ * @param user_ctx User context of the partition owner (NULL in kernel)
+ * @param partition_base Base address (physical) of the partition
+ * @param offset_bytes Offset from base address that the decrypted data
+ * shall be placed
+ * @param byte_count Length of the message (bytes)
+ * @param black_data Pointer to where the encrypted data is stored
+ * @param owner_id
+ *
+ * @return status
+ */
+
+fsl_shw_return_t
+do_scc_decrypt_region(fsl_shw_uco_t * user_ctx,
+ void *partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, const uint8_t * black_data,
+ uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode)
+{
+ scc_return_t scc_ret;
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+
+#ifdef FSL_HAVE_SCC2
+
+#ifdef DIAG_ADAPTOR
+ uint32_t *owner_32 = (uint32_t *) & (owner_id);
+
+ LOG_KDIAG_ARGS
+ ("partition base: %p, offset: %i, count: %i, black data: %p\n",
+ partition_base, offset_bytes, byte_count, (void *)black_data);
+#endif
+
+ (void)user_ctx;
+
+ /* The SCC2 DMA engine will be reading from the black ram, so we need to
+ * make sure that the data is pushed out of the cache. Note that the red
+ * ram is not an issue because it is mapped with the cache disabled.
+ */
+ os_cache_flush_range(black_data, byte_count);
+
+ scc_ret =
+ scc_decrypt_region((uint32_t) partition_base, offset_bytes,
+ byte_count,
+ (uint8_t *) __virt_to_phys(black_data), IV,
+ cypher_mode);
+
+ if (scc_ret == SCC_RET_OK) {
+ retval = FSL_RETURN_OK_S;
+ } else {
+ retval = FSL_RETURN_ERROR_S;
+ }
+
+#else
+ (void)scc_ret;
+#endif /* FSL_HAVE_SCC2 */
+
+ return retval;
+}
+
+#ifdef DIAG_ADAPTOR
+/*!
+ * Dump chain of descriptors to the log.
+ *
+ * @brief Dump descriptor chain
+ *
+ * @param chain Kernel virtual address of start of chain of descriptors
+ *
+ * @return void
+ */
+void km_Dump_Chain(const sah_Desc * chain)
+{
+ while (chain != NULL) {
+ km_Dump_Words("Desc", (unsigned *)chain,
+ 6 /*sizeof(*chain)/sizeof(unsigned) */ );
+ /* place this definition elsewhere */
+ if (chain->ptr1) {
+ if (chain->header & SAH_HDR_LLO) {
+ km_Dump_Region(" Data1", chain->ptr1,
+ chain->len1);
+ } else {
+ km_Dump_Link(" Link1", chain->ptr1);
+ }
+ }
+ if (chain->ptr2) {
+ if (chain->header & SAH_HDR_LLO) {
+ km_Dump_Region(" Data2", chain->ptr2,
+ chain->len2);
+ } else {
+ km_Dump_Link(" Link2", chain->ptr2);
+ }
+ }
+
+ chain = chain->next;
+ }
+}
+
+/*!
+ * Dump chain of links to the log.
+ *
+ * @brief Dump chain of links
+ *
+ * @param prefix Text to put in front of dumped data
+ * @param link Kernel virtual address of start of chain of links
+ *
+ * @return void
+ */
+static void km_Dump_Link(const char *prefix, const sah_Link * link)
+{
+ while (link != NULL) {
+ km_Dump_Words(prefix, (unsigned *)link,
+ 3 /* # words in h/w link */ );
+ if (link->flags & SAH_STORED_KEY_INFO) {
+#ifdef CAN_DUMP_SCC_DATA
+ uint32_t len;
+#endif
+
+#ifdef CAN_DUMP_SCC_DATA
+ {
+ char buf[50];
+
+ scc_get_slot_info(link->ownerid, link->slot, (uint32_t *) & link->data, /* RED key address */
+ &len); /* key length */
+ sprintf(buf, " SCC slot %d: ", link->slot);
+ km_Dump_Words(buf,
+ (void *)IO_ADDRESS((uint32_t)
+ link->data),
+ link->len / 4);
+ }
+#else
+ sprintf(Diag_msg, " SCC slot %d", link->slot);
+ LOG_KDIAG(Diag_msg);
+#endif
+ } else if (link->data != NULL) {
+ km_Dump_Region(" Data", link->data, link->len);
+ }
+
+ link = link->next;
+ }
+}
+
+/*!
+ * Dump given region of data to the log.
+ *
+ * @brief Dump data
+ *
+ * @param prefix Text to put in front of dumped data
+ * @param data Kernel virtual address of start of region to dump
+ * @param length Amount of data to dump
+ *
+ * @return void
+*/
+void km_Dump_Region(const char *prefix, const unsigned char *data,
+ unsigned length)
+{
+ unsigned count;
+ char *output;
+ unsigned data_len;
+
+ sprintf(Diag_msg, "%s (%08X,%u):", prefix, (uint32_t) data, length);
+
+ /* Restrict amount of data to dump */
+ if (length > MAX_DUMP) {
+ data_len = MAX_DUMP;
+ } else {
+ data_len = length;
+ }
+
+ /* We've already printed some text in output buffer, skip over it */
+ output = Diag_msg + strlen(Diag_msg);
+
+ for (count = 0; count < data_len; count++) {
+ if (count % 4 == 0) {
+ *output++ = ' ';
+ }
+ sprintf(output, "%02X", *data++);
+ output += 2;
+ }
+
+ LOG_KDIAG(Diag_msg);
+}
+
+/*!
+ * Dump given wors of data to the log.
+ *
+ * @brief Dump data
+ *
+ * @param prefix Text to put in front of dumped data
+ * @param data Kernel virtual address of start of region to dump
+ * @param word_count Amount of data to dump
+ *
+ * @return void
+*/
+void km_Dump_Words(const char *prefix, const unsigned *data,
+ unsigned word_count)
+{
+ char *output;
+
+ sprintf(Diag_msg, "%s (%08X,%uw): ", prefix, (uint32_t) data,
+ word_count);
+
+ /* We've already printed some text in output buffer, skip over it */
+ output = Diag_msg + strlen(Diag_msg);
+
+ while (word_count--) {
+ sprintf(output, "%08X ", *data++);
+ output += 9;
+ }
+
+ LOG_KDIAG(Diag_msg);
+}
+#endif
diff --git a/drivers/mxc/security/sahara2/sah_driver_interface.c b/drivers/mxc/security/sahara2/sah_driver_interface.c
new file mode 100644
index 000000000000..cf01c5f8bfe0
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_driver_interface.c
@@ -0,0 +1,2185 @@
+/*
+ * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+* @file sah_driver_interface.c
+*
+* @brief Provides a Linux Kernel Module interface to the SAHARA h/w device.
+*
+*/
+
+/* SAHARA Includes */
+#include <sah_driver_common.h>
+#include <sah_kernel.h>
+#include <sah_memory_mapper.h>
+#include <sah_queue_manager.h>
+#include <sah_status_manager.h>
+#include <sah_interrupt_handler.h>
+#include <sah_hardware_interface.h>
+#include <fsl_shw_keystore.h>
+#include <adaptor.h>
+#ifdef FSL_HAVE_SCC
+#include <linux/mxc_scc_driver.h>
+#else
+#include <linux/mxc_scc2_driver.h>
+#endif
+
+#ifdef DIAG_DRV_IF
+#include <diagnostic.h>
+#endif
+
+#if defined(CONFIG_DEVFS_FS) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+#include <linux/devfs_fs_kernel.h>
+#else
+#include <linux/proc_fs.h>
+#endif
+
+#ifdef PERF_TEST
+#define interruptible_sleep_on(x) sah_Handle_Interrupt()
+#endif
+
+#define TEST_MODE_OFF 1
+#define TEST_MODE_ON 2
+
+/*! Version register on first deployments */
+#define SAHARA_VERSION2 2
+/*! Version register on MX27 */
+#define SAHARA_VERSION3 3
+/*! Version register on MXC92323 */
+#define SAHARA_VERSION4 4
+
+/******************************************************************************
+* Module function declarations
+******************************************************************************/
+
+OS_DEV_INIT_DCL(sah_init);
+OS_DEV_SHUTDOWN_DCL(sah_cleanup);
+OS_DEV_OPEN_DCL(sah_open);
+OS_DEV_CLOSE_DCL(sah_release);
+OS_DEV_IOCTL_DCL(sah_ioctl);
+OS_DEV_MMAP_DCL(sah_mmap);
+
+static os_error_code sah_handle_get_capabilities(fsl_shw_uco_t* user_ctx,
+ uint32_t info);
+
+static void sah_user_callback(fsl_shw_uco_t * user_ctx);
+static os_error_code sah_handle_scc_sfree(fsl_shw_uco_t* user_ctx,
+ uint32_t info);
+static os_error_code sah_handle_scc_sstatus(fsl_shw_uco_t* user_ctx,
+ uint32_t info);
+static os_error_code sah_handle_scc_drop_perms(fsl_shw_uco_t* user_ctx,
+ uint32_t info);
+static os_error_code sah_handle_scc_encrypt(fsl_shw_uco_t* user_ctx,
+ uint32_t info);
+static os_error_code sah_handle_scc_decrypt(fsl_shw_uco_t* user_ctx,
+ uint32_t info);
+
+#ifdef FSL_HAVE_SCC2
+static fsl_shw_return_t register_user_partition(fsl_shw_uco_t * user_ctx,
+ uint32_t user_base,
+ void *kernel_base);
+static fsl_shw_return_t deregister_user_partition(fsl_shw_uco_t * user_ctx,
+ uint32_t user_base);
+#endif
+
+static os_error_code sah_handle_sk_slot_alloc(uint32_t info);
+static os_error_code sah_handle_sk_slot_dealloc(uint32_t info);
+static os_error_code sah_handle_sk_slot_load(uint32_t info);
+static os_error_code sah_handle_sk_slot_read(uint32_t info);
+static os_error_code sah_handle_sk_slot_decrypt(uint32_t info);
+static os_error_code sah_handle_sk_slot_encrypt(uint32_t info);
+
+/*! Boolean flag for whether interrupt handler needs to be released on exit */
+static unsigned interrupt_registered;
+
+static int handle_sah_ioctl_dar(fsl_shw_uco_t * filp, uint32_t user_space_desc);
+
+#if !defined(CONFIG_DEVFS_FS) || (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static int sah_read_procfs(char *buf,
+ char **start,
+ off_t offset, int count, int *eof, void *data);
+
+static int sah_write_procfs(struct file *file, const char __user * buffer,
+ unsigned long count, void *data);
+#endif
+
+#if defined(CONFIG_DEVFS_FS) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+
+/* This is a handle to the sahara DEVFS entry. */
+static devfs_handle_t Sahara_devfs_handle;
+
+#else
+
+/* Major number assigned to our device driver */
+static int Major;
+
+/* This is a handle to the sahara PROCFS entry */
+static struct proc_dir_entry *Sahara_procfs_handle;
+
+#endif
+
+uint32_t sah_hw_version;
+extern void *sah_virt_base;
+
+/* This is the wait queue to this driver. Linux declaration. */
+DECLARE_WAIT_QUEUE_HEAD(Wait_queue);
+
+/* This is a global variable that is used to track how many times the device
+ * has been opened simultaneously. */
+#ifdef DIAG_DRV_IF
+static int Device_in_use = 0;
+#endif
+
+/* This is the system keystore object */
+fsl_shw_kso_t system_keystore;
+
+/*!
+ * OS-dependent handle used for registering user interface of a driver.
+ */
+static os_driver_reg_t reg_handle;
+
+#ifdef DIAG_DRV_IF
+/* This is for sprintf() to use when constructing output. */
+#define DIAG_MSG_SIZE 1024
+static char Diag_msg[DIAG_MSG_SIZE];
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18))
+/** Pointer to Sahara clock information. Initialized during os_dev_init(). */
+static struct clk *sah_clk;
+#endif
+
+/*!
+*******************************************************************************
+* This function gets called when the module is inserted (insmod) into the
+* running kernel.
+*
+* @brief SAHARA device initialisation function.
+*
+* @return 0 on success
+* @return -EBUSY if the device or proc file entry cannot be created.
+* @return OS_ERROR_NO_MEMORY_S if kernel memory could not be allocated.
+* @return OS_ERROR_FAIL_S if initialisation of proc entry failed
+*/
+OS_DEV_INIT(sah_init)
+{
+ /* Status variable */
+ int os_error_code = 0;
+ uint32_t sah_phys_base = SAHARA_BASE_ADDR;
+
+
+ interrupt_registered = 0;
+
+ /* Enable the SAHARA Clocks */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA : Enabling the IPG and AHB clocks\n")
+#endif /*DIAG_DRV_IF */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
+ mxc_clks_enable(SAHARA2_CLK);
+#else
+ {
+ sah_clk = clk_get(NULL, "sahara_clk");
+ if (IS_ERR(sah_clk))
+ os_error_code = PTR_ERR(sah_clk);
+ else
+ clk_enable(sah_clk);
+ }
+#endif
+
+ if (cpu_is_mx53())
+ sah_phys_base -= 0x20000000;
+
+ sah_virt_base = (void *)ioremap(sah_phys_base, SZ_16K);
+ if (sah_virt_base == NULL) {
+ os_printk(KERN_ERR
+ "SAHARA: Register mapping failed\n");
+ os_error_code = OS_ERROR_FAIL_S;
+ }
+
+ if (os_error_code == OS_ERROR_OK_S) {
+ sah_hw_version = sah_HW_Read_Version();
+ os_printk("Sahara HW Version is 0x%08x\n", sah_hw_version);
+
+ /* verify code and hardware are version compatible */
+ if ((sah_hw_version != SAHARA_VERSION2)
+ && (sah_hw_version != SAHARA_VERSION3)) {
+ if (((sah_hw_version >> 8) & 0xff) != SAHARA_VERSION4) {
+ os_printk
+ ("Sahara HW Version was not expected value.\n");
+ os_error_code = OS_ERROR_FAIL_S;
+ }
+ }
+ }
+
+ if (os_error_code == OS_ERROR_OK_S) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Calling sah_Init_Mem_Map to initialise "
+ "memory subsystem.");
+#endif
+ /* Do any memory-routine initialization */
+ os_error_code = sah_Init_Mem_Map();
+ }
+
+ if (os_error_code == OS_ERROR_OK_S) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Calling sah_HW_Reset() to Initialise the Hardware.");
+#endif
+ /* Initialise the hardware */
+ os_error_code = sah_HW_Reset();
+ if (os_error_code != OS_ERROR_OK_S) {
+ os_printk
+ ("sah_HW_Reset() failed to Initialise the Hardware.\n");
+ }
+
+ }
+
+ if (os_error_code == OS_ERROR_OK_S) {
+#if defined(CONFIG_DEVFS_FS) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+ /* Register the DEVFS entry */
+ Sahara_devfs_handle = devfs_register(NULL,
+ SAHARA_DEVICE_SHORT,
+ DEVFS_FL_AUTO_DEVNUM,
+ 0, 0,
+ SAHARA_DEVICE_MODE,
+ &Fops, NULL);
+ if (Sahara_devfs_handle == NULL) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG
+ ("Registering the DEVFS character device failed.");
+#endif /* DIAG_DRV_IF */
+ os_error_code = -EBUSY;
+ }
+#else /* CONFIG_DEVFS_FS */
+ /* Create the PROCFS entry. This is used to report the assigned device
+ * major number back to user-space. */
+#if 1
+ Sahara_procfs_handle = create_proc_entry(SAHARA_DEVICE_SHORT, 0700, /* default mode */
+ NULL); /* parent dir */
+ if (Sahara_procfs_handle == NULL) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Registering the PROCFS interface failed.");
+#endif /* DIAG_DRV_IF */
+ os_error_code = OS_ERROR_FAIL_S;
+ } else {
+ Sahara_procfs_handle->nlink = 1;
+ Sahara_procfs_handle->data = 0;
+ Sahara_procfs_handle->read_proc = sah_read_procfs;
+ Sahara_procfs_handle->write_proc = sah_write_procfs;
+ }
+#endif /* #if 1 */
+ }
+
+ if (os_error_code == OS_ERROR_OK_S) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG
+ ("Calling sah_Queue_Manager_Init() to Initialise the Queue "
+ "Manager.");
+#endif
+ /* Initialise the Queue Manager */
+ if (sah_Queue_Manager_Init() != FSL_RETURN_OK_S) {
+ os_error_code = -ENOMEM;
+ }
+ }
+#ifndef SAHARA_POLL_MODE
+ if (os_error_code == OS_ERROR_OK_S) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Calling sah_Intr_Init() to Initialise the Interrupt "
+ "Handler.");
+#endif
+ /* Initialise the Interrupt Handler */
+ os_error_code = sah_Intr_Init(&Wait_queue);
+ if (os_error_code == OS_ERROR_OK_S) {
+ interrupt_registered = 1;
+ }
+ }
+#endif /* ifndef SAHARA_POLL_MODE */
+
+#ifdef SAHARA_POWER_MANAGEMENT
+ if (os_error_code == OS_ERROR_OK_S) {
+ /* set up dynamic power management (dmp) */
+ os_error_code = sah_dpm_init();
+ }
+#endif
+
+ if (os_error_code == OS_ERROR_OK_S) {
+ os_driver_init_registration(reg_handle);
+ os_driver_add_registration(reg_handle, OS_FN_OPEN,
+ OS_DEV_OPEN_REF(sah_open));
+ os_driver_add_registration(reg_handle, OS_FN_IOCTL,
+ OS_DEV_IOCTL_REF(sah_ioctl));
+ os_driver_add_registration(reg_handle, OS_FN_CLOSE,
+ OS_DEV_CLOSE_REF(sah_release));
+ os_driver_add_registration(reg_handle, OS_FN_MMAP,
+ OS_DEV_MMAP_REF(sah_mmap));
+
+ os_error_code =
+ os_driver_complete_registration(reg_handle, Major,
+ "sahara");
+
+ if (os_error_code < OS_ERROR_OK_S) {
+#ifdef DIAG_DRV_IF
+ snprintf(Diag_msg, DIAG_MSG_SIZE,
+ "Registering the regular "
+ "character device failed with error code: %d\n",
+ os_error_code);
+ LOG_KDIAG(Diag_msg);
+#endif
+ }
+ }
+#endif /* CONFIG_DEVFS_FS */
+
+ if (os_error_code == OS_ERROR_OK_S) {
+ /* set up the system keystore, using the default keystore handler */
+ fsl_shw_init_keystore_default(&system_keystore);
+
+ if (fsl_shw_establish_keystore(NULL, &system_keystore)
+ == FSL_RETURN_OK_S) {
+ os_error_code = OS_ERROR_OK_S;
+ } else {
+ os_error_code = OS_ERROR_FAIL_S;
+ }
+
+ if (os_error_code != OS_ERROR_OK_S) {
+#ifdef DIAG_DRV_IF
+ snprintf(Diag_msg, DIAG_MSG_SIZE,
+ "Registering the system keystore "
+ "failed with error code: %d\n", os_error_code);
+ LOG_KDIAG(Diag_msg);
+#endif
+ }
+ }
+
+ if (os_error_code != OS_ERROR_OK_S) {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+ cleanup_module();
+#else
+ sah_cleanup();
+#endif
+ }
+#ifdef DIAG_DRV_IF
+ else {
+ LOG_KDIAG_ARGS("Sahara major node is %d\n", Major);
+ }
+#endif
+
+/* Disabling the Clock after the driver has been registered fine.
+ This is done to save power when Sahara is not in use.*/
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA : Disabling the clocks\n")
+#endif /* DIAG_DRV_IF */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ mxc_clks_disable(SAHARA2_CLK);
+#else
+ {
+ if (IS_ERR(sah_clk))
+ os_error_code = PTR_ERR(sah_clk);
+ else
+ clk_disable(sah_clk);
+ }
+#endif
+
+ os_dev_init_return(os_error_code);
+}
+
+/*!
+*******************************************************************************
+* This function gets called when the module is removed (rmmod) from the running
+* kernel.
+*
+* @brief SAHARA device clean-up function.
+*
+* @return void
+*/
+OS_DEV_SHUTDOWN(sah_cleanup)
+{
+ int ret_val = 0;
+
+ printk(KERN_ALERT "Sahara going into cleanup\n");
+
+ /* clear out the system keystore */
+ fsl_shw_release_keystore(NULL, &system_keystore);
+
+ /* Unregister the device */
+#if defined(CONFIG_DEVFS_FS) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+ devfs_unregister(Sahara_devfs_handle);
+#else
+
+ if (Sahara_procfs_handle != NULL) {
+ remove_proc_entry(SAHARA_DEVICE_SHORT, NULL);
+ }
+
+ if (Major >= 0) {
+ ret_val = os_driver_remove_registration(reg_handle);
+ }
+#ifdef DIAG_DRV_IF
+ if (ret_val < 0) {
+ snprintf(Diag_msg, DIAG_MSG_SIZE, "Error while attempting to "
+ "unregister the device: %d\n", ret_val);
+ LOG_KDIAG(Diag_msg);
+ }
+#endif
+
+#endif /* CONFIG_DEVFS_FS */
+ sah_Queue_Manager_Close();
+
+#ifndef SAHARA_POLL_MODE
+ if (interrupt_registered) {
+ sah_Intr_Release();
+ interrupt_registered = 0;
+ }
+#endif
+ sah_Stop_Mem_Map();
+#ifdef SAHARA_POWER_MANAGEMENT
+ sah_dpm_close();
+#endif
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA : Disabling the clocks\n")
+#endif /* DIAG_DRV_IF */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
+ mxc_clks_disable(SAHARA2_CLK);
+#else
+ {
+ if (IS_ERR(sah_clk))
+ ret_val = PTR_ERR(sah_clk);
+ else
+ clk_disable(sah_clk);
+ clk_put(sah_clk);
+ }
+#endif
+
+ os_dev_shutdown_return(ret_val);
+}
+
+/*!
+*******************************************************************************
+* This function simply increments the module usage count.
+*
+* @brief SAHARA device open function.
+*
+* @param inode Part of the kernel prototype.
+* @param file Part of the kernel prototype.
+*
+* @return 0 - Always returns 0 since any number of calls to this function are
+* allowed.
+*
+*/
+OS_DEV_OPEN(sah_open)
+{
+
+#if defined(LINUX_VERSION) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10))
+ MOD_INC_USE_COUNT;
+#endif
+
+#ifdef DIAG_DRV_IF
+ Device_in_use++;
+ snprintf(Diag_msg, DIAG_MSG_SIZE,
+ "Incrementing module use count to: %d ", Device_in_use);
+ LOG_KDIAG(Diag_msg);
+#endif
+
+ os_dev_set_user_private(NULL);
+
+ /* Return 0 to indicate success */
+ os_dev_open_return(0);
+}
+
+/*!
+*******************************************************************************
+* This function simply decrements the module usage count.
+*
+* @brief SAHARA device release function.
+*
+* @param inode Part of the kernel prototype.
+* @param file Part of the kernel prototype.
+*
+* @return 0 - Always returns 0 since this function does not fail.
+*/
+OS_DEV_CLOSE(sah_release)
+{
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+#if defined(LINUX_VERSION) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10))
+ MOD_DEC_USE_COUNT;
+#endif
+
+#ifdef DIAG_DRV_IF
+ Device_in_use--;
+ snprintf(Diag_msg, DIAG_MSG_SIZE,
+ "Decrementing module use count to: %d ", Device_in_use);
+ LOG_KDIAG(Diag_msg);
+#endif
+
+ if (user_ctx != NULL) {
+ sah_handle_deregistration(user_ctx);
+ os_free_memory(user_ctx);
+ os_dev_set_user_private(NULL);
+ }
+
+ /* Return 0 to indicate success */
+ os_dev_close_return(OS_ERROR_OK_S);
+}
+
+/*!
+*******************************************************************************
+* This function provides the IO Controls for the SAHARA driver. Three IO
+* Controls are supported:
+*
+* SAHARA_HWRESET and
+* SAHARA_SET_HA
+* SAHARA_CHK_TEST_MODE
+*
+* @brief SAHARA device IO Control function.
+*
+* @param inode Part of the kernel prototype.
+* @param filp Part of the kernel prototype.
+* @param cmd Part of the kernel prototype.
+* @param arg Part of the kernel prototype.
+*
+* @return 0 on success
+* @return -EBUSY if the HA bit could not be set due to busy hardware.
+* @return -ENOTTY if an unsupported IOCTL was attempted on the device.
+* @return -EFAULT if put_user() fails
+*/
+OS_DEV_IOCTL(sah_ioctl)
+{
+ int status = 0;
+ int test_mode;
+
+ switch (os_dev_get_ioctl_op()) {
+ case SAHARA_HWRESET:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_HWRESET IOCTL.");
+#endif
+ /* We need to reset the hardware. */
+ sah_HW_Reset();
+
+ /* Mark all the entries in the Queue Manager's queue with state
+ * SAH_STATE_RESET.
+ */
+ sah_Queue_Manager_Reset_Entries();
+
+ /* Wake up all sleeping write() calls. */
+ wake_up_interruptible(&Wait_queue);
+ break;
+#ifdef SAHARA_HA_ENABLED
+ case SAHARA_SET_HA:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SET_HA IOCTL.");
+#endif /* DIAG_DRV_IF */
+ if (sah_HW_Set_HA() == ERR_INTERNAL) {
+ status = -EBUSY;
+ }
+ break;
+#endif /* SAHARA_HA_ENABLED */
+
+ case SAHARA_CHK_TEST_MODE:
+ /* load test_mode */
+ test_mode = TEST_MODE_OFF;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_CHECK_TEST_MODE IOCTL.");
+ test_mode = TEST_MODE_ON;
+#endif /* DIAG_DRV_IF */
+#if defined(KERNEL_TEST) || defined(PERF_TEST)
+ test_mode = TEST_MODE_ON;
+#endif /* KERNEL_TEST || PERF_TEST */
+ /* copy test_mode back to user space. put_user() is Linux fn */
+ /* compiler warning `register': no problem found so ignored */
+ status = put_user(test_mode, (int *)os_dev_get_ioctl_arg());
+ break;
+
+ case SAHARA_DAR:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_DAR IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ if (user_ctx != NULL) {
+ status =
+ handle_sah_ioctl_dar(user_ctx,
+ os_dev_get_ioctl_arg
+ ());
+ } else {
+ status = OS_ERROR_FAIL_S;
+ }
+
+ }
+ break;
+
+ case SAHARA_GET_RESULTS:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_GET_RESULTS IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ if (user_ctx != NULL) {
+ status =
+ sah_get_results_pointers(user_ctx,
+ os_dev_get_ioctl_arg
+ ());
+ } else {
+ status = OS_ERROR_FAIL_S;
+ }
+ }
+ break;
+
+ case SAHARA_REGISTER:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_REGISTER IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ if (user_ctx != NULL) {
+ status = OS_ERROR_FAIL_S; /* already registered */
+ } else {
+ user_ctx =
+ os_alloc_memory(sizeof(fsl_shw_uco_t),
+ GFP_KERNEL);
+ if (user_ctx == NULL) {
+ status = OS_ERROR_NO_MEMORY_S;
+ } else {
+ /* Copy UCO from user, but only as big as the common UCO */
+ if (os_copy_from_user(user_ctx,
+ (void *)
+ os_dev_get_ioctl_arg
+ (),
+ offsetof
+ (fsl_shw_uco_t,
+ result_pool))) {
+ status = OS_ERROR_FAIL_S;
+ } else {
+ os_dev_set_user_private
+ (user_ctx);
+ status =
+ sah_handle_registration
+ (user_ctx);
+ }
+ }
+ }
+ }
+ break;
+
+ /* This ioctl cmd should disappear in favor of a close() routine. */
+ case SAHARA_DEREGISTER:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_DEREGISTER IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ if (user_ctx == NULL) {
+ status = OS_ERROR_FAIL_S;
+ } else {
+ status = sah_handle_deregistration(user_ctx);
+ os_free_memory(user_ctx);
+ os_dev_set_user_private(NULL);
+ }
+ }
+ break;
+ case SAHARA_SCC_DROP_PERMS:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SCC_DROP_PERMS IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ /* drop permissions on the specified partition */
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ status =
+ sah_handle_scc_drop_perms(user_ctx,
+ os_dev_get_ioctl_arg());
+ }
+ break;
+
+ case SAHARA_SCC_SFREE:
+ /* Unmap the specified partition from the users space, and then
+ * free it for use by someone else.
+ */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SCC_SFREE IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ status =
+ sah_handle_scc_sfree(user_ctx,
+ os_dev_get_ioctl_arg());
+ }
+ break;
+
+ case SAHARA_SCC_SSTATUS:
+ /* Unmap the specified partition from the users space, and then
+ * free it for use by someone else.
+ */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SCC_SSTATUS IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ status =
+ sah_handle_scc_sstatus(user_ctx,
+ os_dev_get_ioctl_arg());
+ }
+ break;
+
+ case SAHARA_SCC_ENCRYPT:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SCC_ENCRYPT IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ status =
+ sah_handle_scc_encrypt(user_ctx,
+ os_dev_get_ioctl_arg());
+ }
+ break;
+
+ case SAHARA_SCC_DECRYPT:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SCC_DECRYPT IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ status =
+ sah_handle_scc_decrypt(user_ctx,
+ os_dev_get_ioctl_arg());
+ }
+ break;
+
+ case SAHARA_SK_ALLOC:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SK_ALLOC IOCTL.");
+#endif /* DIAG_DRV_IF */
+ status = sah_handle_sk_slot_alloc(os_dev_get_ioctl_arg());
+ break;
+
+ case SAHARA_SK_DEALLOC:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SK_DEALLOC IOCTL.");
+#endif /* DIAG_DRV_IF */
+ status = sah_handle_sk_slot_dealloc(os_dev_get_ioctl_arg());
+ break;
+
+ case SAHARA_SK_LOAD:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SK_LOAD IOCTL.");
+#endif /* DIAG_DRV_IF */
+ status = sah_handle_sk_slot_load(os_dev_get_ioctl_arg());
+ break;
+ case SAHARA_SK_READ:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SK_READ IOCTL.");
+#endif /* DIAG_DRV_IF */
+ status = sah_handle_sk_slot_read(os_dev_get_ioctl_arg());
+ break;
+
+ case SAHARA_SK_SLOT_DEC:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SK_SLOT_DECRYPT IOCTL.");
+#endif /* DIAG_DRV_IF */
+ status = sah_handle_sk_slot_decrypt(os_dev_get_ioctl_arg());
+ break;
+
+ case SAHARA_SK_SLOT_ENC:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SK_SLOT_ENCRYPT IOCTL.");
+#endif /* DIAG_DRV_IF */
+ status = sah_handle_sk_slot_encrypt(os_dev_get_ioctl_arg());
+ break;
+ case SAHARA_GET_CAPS:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_GET_CAPS IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ status =
+ sah_handle_get_capabilities(user_ctx,
+ os_dev_get_ioctl_arg());
+ }
+ break;
+
+ default:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Unknown SAHARA IOCTL.");
+#endif /* DIAG_DRV_IF */
+ status = OS_ERROR_FAIL_S;
+ }
+
+ os_dev_ioctl_return(status);
+}
+
+/* Fill in the user's capabilities structure */
+static os_error_code sah_handle_get_capabilities(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code status = OS_ERROR_FAIL_S;
+ fsl_shw_pco_t capabilities;
+
+ status = os_copy_from_user(&capabilities, (void *)info,
+ sizeof(fsl_shw_pco_t));
+
+ if (status != OS_ERROR_OK_S) {
+ goto out;
+ }
+
+ if (get_capabilities(user_ctx, &capabilities) == FSL_RETURN_OK_S) {
+ status = os_copy_to_user((void *)info, &capabilities,
+ sizeof(fsl_shw_pco_t));
+ }
+
+ out:
+ return status;
+}
+
+#ifdef FSL_HAVE_SCC2
+
+/* Find the kernel-mode address of the partition.
+ * This can then be passed to the SCC functions.
+ */
+void *lookup_user_partition(fsl_shw_uco_t * user_ctx, uint32_t user_base)
+{
+ /* search through the partition chain to find one that matches the user base
+ * address.
+ */
+ fsl_shw_spo_t *curr = (fsl_shw_spo_t *) user_ctx->partition;
+
+ while (curr != NULL) {
+ if (curr->user_base == user_base) {
+ return curr->kernel_base;
+ }
+ curr = (fsl_shw_spo_t *) curr->next;
+ }
+ return NULL;
+}
+
+/* user_base: userspace base address of the partition
+ * kernel_base: kernel mode base address of the partition
+ */
+static fsl_shw_return_t register_user_partition(fsl_shw_uco_t * user_ctx,
+ uint32_t user_base,
+ void *kernel_base)
+{
+ fsl_shw_spo_t *partition_info;
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ if (user_ctx == NULL) {
+ goto out;
+ }
+
+ partition_info = os_alloc_memory(sizeof(fsl_shw_spo_t), GFP_KERNEL);
+
+ if (partition_info == NULL) {
+ goto out;
+ }
+
+ /* stuff the partition info, then put it at the front of the chain */
+ partition_info->user_base = user_base;
+ partition_info->kernel_base = kernel_base;
+ partition_info->next = user_ctx->partition;
+
+ user_ctx->partition = (struct fsl_shw_spo_t *)partition_info;
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS
+ ("partition with user_base=%p, kernel_base=%p registered.",
+ (void *)user_base, kernel_base);
+#endif
+
+ ret = FSL_RETURN_OK_S;
+
+ out:
+
+ return ret;
+}
+
+/* if the partition is in the users list, remove it */
+static fsl_shw_return_t deregister_user_partition(fsl_shw_uco_t * user_ctx,
+ uint32_t user_base)
+{
+ fsl_shw_spo_t *curr = (fsl_shw_spo_t *) user_ctx->partition;
+ fsl_shw_spo_t *last = (fsl_shw_spo_t *) user_ctx->partition;
+
+ while (curr != NULL) {
+ if (curr->user_base == user_base) {
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS
+ ("deregister_user_partition: partition with "
+ "user_base=%p, kernel_base=%p deregistered.\n",
+ (void *)curr->user_base, curr->kernel_base);
+#endif
+
+ if (last == curr) {
+ user_ctx->partition = curr->next;
+ os_free_memory(curr);
+ return FSL_RETURN_OK_S;
+ } else {
+ last->next = curr->next;
+ os_free_memory(curr);
+ return FSL_RETURN_OK_S;
+ }
+ }
+ last = curr;
+ curr = (fsl_shw_spo_t *) curr->next;
+ }
+
+ return FSL_RETURN_ERROR_S;
+}
+
+#endif /* FSL_HAVE_SCC2 */
+
+static os_error_code sah_handle_scc_drop_perms(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code status = OS_ERROR_NO_MEMORY_S;
+#ifdef FSL_HAVE_SCC2
+ scc_return_t scc_ret;
+ scc_partition_info_t partition_info;
+ void *kernel_base;
+
+ status =
+ os_copy_from_user(&partition_info, (void *)info,
+ sizeof(partition_info));
+
+ if (status != OS_ERROR_OK_S) {
+ goto out;
+ }
+
+ /* validate that the user owns this partition, and look up its handle */
+ kernel_base = lookup_user_partition(user_ctx, partition_info.user_base);
+
+ if (kernel_base == NULL) {
+ status = OS_ERROR_FAIL_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("_scc_drop_perms(): failed to find partition\n");
+#endif
+ goto out;
+ }
+
+ /* call scc driver to perform the drop */
+ scc_ret = scc_diminish_permissions(kernel_base,
+ partition_info.permissions);
+ if (scc_ret == SCC_RET_OK) {
+ status = OS_ERROR_OK_S;
+ } else {
+ status = OS_ERROR_FAIL_S;
+ }
+
+ out:
+#endif /* FSL_HAVE_SCC2 */
+ return status;
+}
+
+static os_error_code sah_handle_scc_sfree(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code status = OS_ERROR_NO_MEMORY_S;
+#ifdef FSL_HAVE_SCC2
+ {
+ scc_partition_info_t partition_info;
+ void *kernel_base;
+ int ret;
+
+ status =
+ os_copy_from_user(&partition_info, (void *)info,
+ sizeof(partition_info));
+
+ /* check that the copy was successful */
+ if (status != OS_ERROR_OK_S) {
+ goto out;
+ }
+
+ /* validate that the user owns this partition, and look up its handle */
+ kernel_base =
+ lookup_user_partition(user_ctx, partition_info.user_base);
+
+ if (kernel_base == NULL) {
+ status = OS_ERROR_FAIL_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("failed to find partition\n");
+#endif /*DIAG_DRV_IF */
+ goto out;
+ }
+
+ /* Unmap the memory region (see sys_munmap in mmap.c) */
+ ret = unmap_user_memory(partition_info.user_base, 8192);
+
+ /* If the memory was successfully released */
+ if (ret == OS_ERROR_OK_S) {
+
+ /* release the partition */
+ scc_release_partition(kernel_base);
+
+ /* and remove it from the users context */
+ deregister_user_partition(user_ctx,
+ partition_info.user_base);
+
+ status = OS_ERROR_OK_S;
+ }
+ }
+ out:
+#endif /* FSL_HAVE_SCC2 */
+ return status;
+}
+
+static os_error_code sah_handle_scc_sstatus(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code status = OS_ERROR_NO_MEMORY_S;
+#ifdef FSL_HAVE_SCC2
+ {
+ scc_partition_info_t partition_info;
+ void *kernel_base;
+
+ status =
+ os_copy_from_user(&partition_info, (void *)info,
+ sizeof(partition_info));
+
+ /* check that the copy was successful */
+ if (status != OS_ERROR_OK_S) {
+ goto out;
+ }
+
+ /* validate that the user owns this partition, and look up its handle */
+ kernel_base =
+ lookup_user_partition(user_ctx, partition_info.user_base);
+
+ if (kernel_base == NULL) {
+ status = OS_ERROR_FAIL_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("failed to find partition\n");
+#endif /*DIAG_DRV_IF */
+ goto out;
+ }
+
+ partition_info.status = scc_partition_status(kernel_base);
+
+ status =
+ os_copy_to_user((void *)info, &partition_info,
+ sizeof(partition_info));
+ }
+ out:
+#endif /* FSL_HAVE_SCC2 */
+ return status;
+}
+
+static os_error_code sah_handle_scc_encrypt(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code os_err = OS_ERROR_FAIL_S;
+#ifdef FSL_HAVE_SCC2
+ {
+ fsl_shw_return_t retval;
+ scc_region_t region_info;
+ void *page_ctx = NULL;
+ void *black_addr = NULL;
+ void *partition_base = NULL;
+ scc_config_t *scc_configuration;
+
+ os_err =
+ os_copy_from_user(&region_info, (void *)info,
+ sizeof(region_info));
+
+ if (os_err != OS_ERROR_OK_S) {
+ goto out;
+ }
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS
+ ("partition_base: %p, offset: %i, length: %i, black data: %p",
+ (void *)region_info.partition_base, region_info.offset,
+ region_info.length, (void *)region_info.black_data);
+#endif
+
+ /* validate that the user owns this partition, and look up its handle */
+ partition_base = lookup_user_partition(user_ctx,
+ region_info.
+ partition_base);
+
+ if (partition_base == NULL) {
+ retval = FSL_RETURN_ERROR_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("failed to find secure partition\n");
+#endif
+ goto out;
+ }
+
+ /* Check that the memory size requested is correct */
+ scc_configuration = scc_get_configuration();
+ if (region_info.offset + region_info.length >
+ scc_configuration->partition_size_bytes) {
+ retval = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ /* wire down black data */
+ black_addr = wire_user_memory(region_info.black_data,
+ region_info.length, &page_ctx);
+
+ if (black_addr == NULL) {
+ retval = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ retval =
+ do_scc_encrypt_region(NULL, partition_base,
+ region_info.offset,
+ region_info.length, black_addr,
+ region_info.IV,
+ region_info.cypher_mode);
+
+ /* release black data */
+ unwire_user_memory(&page_ctx);
+
+ out:
+ if (os_err == OS_ERROR_OK_S) {
+ /* Return error code */
+ region_info.code = retval;
+ os_err =
+ os_copy_to_user((void *)info, &region_info,
+ sizeof(region_info));
+ }
+ }
+
+#endif
+ return os_err;
+}
+
+static os_error_code sah_handle_scc_decrypt(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code os_err = OS_ERROR_FAIL_S;
+#ifdef FSL_HAVE_SCC2
+ {
+ fsl_shw_return_t retval;
+ scc_region_t region_info;
+ void *page_ctx = NULL;
+ void *black_addr;
+ void *partition_base;
+ scc_config_t *scc_configuration;
+
+ os_err =
+ os_copy_from_user(&region_info, (void *)info,
+ sizeof(region_info));
+
+ if (os_err != OS_ERROR_OK_S) {
+ goto out;
+ }
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS
+ ("partition_base: %p, offset: %i, length: %i, black data: %p",
+ (void *)region_info.partition_base, region_info.offset,
+ region_info.length, (void *)region_info.black_data);
+#endif
+
+ /* validate that the user owns this partition, and look up its handle */
+ partition_base = lookup_user_partition(user_ctx,
+ region_info.
+ partition_base);
+
+ if (partition_base == NULL) {
+ retval = FSL_RETURN_ERROR_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("failed to find partition\n");
+#endif
+ goto out;
+ }
+
+ /* Check that the memory size requested is correct */
+ scc_configuration = scc_get_configuration();
+ if (region_info.offset + region_info.length >
+ scc_configuration->partition_size_bytes) {
+ retval = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ /* wire down black data */
+ black_addr = wire_user_memory(region_info.black_data,
+ region_info.length, &page_ctx);
+
+ if (black_addr == NULL) {
+ retval = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ retval =
+ do_scc_decrypt_region(NULL, partition_base,
+ region_info.offset,
+ region_info.length, black_addr,
+ region_info.IV,
+ region_info.cypher_mode);
+
+ /* release black data */
+ unwire_user_memory(&page_ctx);
+
+ out:
+ if (os_err == OS_ERROR_OK_S) {
+ /* Return error code */
+ region_info.code = retval;
+ os_err =
+ os_copy_to_user((void *)info, &region_info,
+ sizeof(region_info));
+ }
+ }
+
+#endif /* FSL_HAVE_SCC2 */
+ return os_err;
+}
+
+/*****************************************************************************/
+/* fn get_user_smid() */
+/*****************************************************************************/
+uint32_t get_user_smid(void *proc)
+{
+ /*
+ * A real implementation would have some way to handle signed applications
+ * which wouild be assigned distinct SMIDs. For the reference
+ * implementation, we show where this would be determined (here), but
+ * always provide a fixed answer, thus not separating users at all.
+ */
+
+ return 0x42eaae42;
+}
+
+/*!
+*******************************************************************************
+* This function implements the smalloc() function for userspace programs, by
+* making a call to the SCC2 mmap() function that acquires a region of secure
+* memory on behalf of the user, and then maps it into the users memory space.
+* Currently, the only memory size supported is that of a single SCC2 partition.
+* Requests for other sized memory regions will fail.
+*/
+OS_DEV_MMAP(sah_mmap)
+{
+ os_error_code status = OS_ERROR_NO_MEMORY_S;
+
+#ifdef FSL_HAVE_SCC2
+ {
+ scc_return_t scc_ret;
+ fsl_shw_return_t fsl_ret;
+ uint32_t partition_registered = FALSE;
+
+ uint32_t user_base;
+ void *partition_base;
+ uint32_t smid;
+ scc_config_t *scc_configuration;
+
+ int part_no = -1;
+ uint32_t part_phys;
+
+ fsl_shw_uco_t *user_ctx =
+ (fsl_shw_uco_t *) os_dev_get_user_private();
+
+ /* Make sure that the user context is valid */
+ if (user_ctx == NULL) {
+ user_ctx =
+ os_alloc_memory(sizeof(*user_ctx), GFP_KERNEL);
+
+ if (user_ctx == NULL) {
+ status = OS_ERROR_NO_MEMORY_S;
+ goto out;
+ }
+
+ sah_handle_registration(user_ctx);
+ os_dev_set_user_private(user_ctx);
+ }
+
+ /* Determine the size of a secure partition */
+ scc_configuration = scc_get_configuration();
+
+ /* Check that the memory size requested is equal to the partition
+ * size, and that the requested destination is on a page boundary.
+ */
+ if (((os_mmap_user_base() % PAGE_SIZE) != 0) ||
+ (os_mmap_memory_size() !=
+ scc_configuration->partition_size_bytes)) {
+ status = OS_ERROR_BAD_ARG_S;
+ goto out;
+ }
+
+ /* Retrieve the SMID associated with the user */
+ smid = get_user_smid(user_ctx->process);
+
+ /* Attempt to allocate a secure partition */
+ scc_ret =
+ scc_allocate_partition(smid, &part_no, &partition_base,
+ &part_phys);
+ if (scc_ret != SCC_RET_OK) {
+ pr_debug
+ ("SCC mmap() request failed to allocate partition;"
+ " error %d\n", status);
+ status = OS_ERROR_FAIL_S;
+ goto out;
+ }
+
+ pr_debug("scc_mmap() acquired partition %d at %08x\n",
+ part_no, part_phys);
+
+ /* Record partition info in the user context */
+ user_base = os_mmap_user_base();
+ fsl_ret =
+ register_user_partition(user_ctx, user_base,
+ partition_base);
+
+ if (fsl_ret != FSL_RETURN_OK_S) {
+ pr_debug
+ ("SCC mmap() request failed to register partition with user"
+ " context, error: %d\n", fsl_ret);
+ status = OS_ERROR_FAIL_S;
+ }
+
+ partition_registered = TRUE;
+
+ status = map_user_memory(os_mmap_memory_ctx(), part_phys,
+ os_mmap_memory_size());
+
+#ifdef SHW_DEBUG
+ if (status == OS_ERROR_OK_S) {
+ LOG_KDIAG_ARGS
+ ("Partition allocated: user_base=%p, partition_base=%p.",
+ (void *)user_base, partition_base);
+ }
+#endif
+
+ out:
+ /* If there is an error it has to be handled here */
+ if (status != OS_ERROR_OK_S) {
+ /* if the partition was registered with the user, unregister it. */
+ if (partition_registered == TRUE) {
+ deregister_user_partition(user_ctx, user_base);
+ }
+
+ /* if the partition was allocated, deallocate it */
+ if (partition_base != NULL) {
+ scc_release_partition(partition_base);
+ }
+ }
+ }
+#endif /* FSL_HAVE_SCC2 */
+
+ return status;
+}
+
+/* Find the physical address of a key stored in the system keystore */
+fsl_shw_return_t
+system_keystore_get_slot_info(uint64_t owner_id, uint32_t slot,
+ uint32_t * address, uint32_t * slot_size_bytes)
+{
+ fsl_shw_return_t retval;
+ void *kernel_address;
+
+ /* First verify that the key access is valid */
+ retval = system_keystore.slot_verify_access(system_keystore.user_data,
+ owner_id, slot);
+
+ if (retval != FSL_RETURN_OK_S) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("verification failed");
+#endif
+ return retval;
+ }
+
+ if (address != NULL) {
+#ifdef FSL_HAVE_SCC2
+ kernel_address =
+ system_keystore.slot_get_address(system_keystore.user_data,
+ slot);
+ (*address) = scc_virt_to_phys(kernel_address);
+#else
+ kernel_address =
+ system_keystore.slot_get_address((void *)&owner_id, slot);
+ (*address) = (uint32_t) kernel_address;
+#endif
+ }
+
+ if (slot_size_bytes != NULL) {
+#ifdef FSL_HAVE_SCC2
+ *slot_size_bytes =
+ system_keystore.slot_get_slot_size(system_keystore.
+ user_data, slot);
+#else
+ *slot_size_bytes =
+ system_keystore.slot_get_slot_size((void *)&owner_id, slot);
+#endif
+ }
+
+ return retval;
+}
+
+static os_error_code sah_handle_sk_slot_alloc(uint32_t info)
+{
+ scc_slot_t slot_info;
+ os_error_code os_err;
+ scc_return_t scc_ret;
+
+ os_err = os_copy_from_user(&slot_info, (void *)info, sizeof(slot_info));
+ if (os_err == OS_ERROR_OK_S) {
+ scc_ret = keystore_slot_alloc(&system_keystore,
+ slot_info.key_length,
+ slot_info.ownerid,
+ &slot_info.slot);
+ if (scc_ret == SCC_RET_OK) {
+ slot_info.code = FSL_RETURN_OK_S;
+ } else if (scc_ret == SCC_RET_INSUFFICIENT_SPACE) {
+ slot_info.code = FSL_RETURN_NO_RESOURCE_S;
+ } else {
+ slot_info.code = FSL_RETURN_ERROR_S;
+ }
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS("key length: %i, handle: %i\n",
+ slot_info.key_length, slot_info.slot);
+#endif
+
+ /* Return error code and slot info */
+ os_err =
+ os_copy_to_user((void *)info, &slot_info,
+ sizeof(slot_info));
+
+ if (os_err != OS_ERROR_OK_S) {
+ (void)keystore_slot_dealloc(&system_keystore,
+ slot_info.ownerid,
+ slot_info.slot);
+ }
+ }
+
+ return os_err;
+}
+
+static os_error_code sah_handle_sk_slot_dealloc(uint32_t info)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+ scc_slot_t slot_info;
+ os_error_code os_err;
+ scc_return_t scc_ret;
+
+ os_err = os_copy_from_user(&slot_info, (void *)info, sizeof(slot_info));
+
+ if (os_err == OS_ERROR_OK_S) {
+ scc_ret = keystore_slot_dealloc(&system_keystore,
+ slot_info.ownerid,
+ slot_info.slot);
+
+ if (scc_ret == SCC_RET_OK) {
+ ret = FSL_RETURN_OK_S;
+ } else {
+ ret = FSL_RETURN_ERROR_S;
+ }
+ slot_info.code = ret;
+
+ os_err =
+ os_copy_to_user((void *)info, &slot_info,
+ sizeof(slot_info));
+ }
+
+ return os_err;
+}
+
+static os_error_code sah_handle_sk_slot_load(uint32_t info)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+ scc_slot_t slot_info;
+ os_error_code os_err;
+ uint8_t *key = NULL;
+
+ os_err = os_copy_from_user(&slot_info, (void *)info, sizeof(slot_info));
+
+ if (os_err == OS_ERROR_OK_S) {
+ /* Allow slop in alloc in case we are rounding up to word multiple */
+ key = os_alloc_memory(slot_info.key_length + 3, GFP_KERNEL);
+ if (key == NULL) {
+ ret = FSL_RETURN_NO_RESOURCE_S;
+ os_err = OS_ERROR_NO_MEMORY_S;
+ } else {
+ os_err = os_copy_from_user(key, slot_info.key,
+ slot_info.key_length);
+ }
+ }
+
+ if (os_err == OS_ERROR_OK_S) {
+ unsigned key_length = slot_info.key_length;
+
+ /* Round up if necessary, as SCC call wants a multiple of 32-bit
+ * values for the full object being loaded. */
+ if ((key_length & 3) != 0) {
+ key_length += 4 - (key_length & 3);
+ }
+ ret = keystore_slot_load(&system_keystore,
+ slot_info.ownerid, slot_info.slot, key,
+ key_length);
+
+ slot_info.code = ret;
+ os_err =
+ os_copy_to_user((void *)info, &slot_info,
+ sizeof(slot_info));
+ }
+
+ if (key != NULL) {
+ memset(key, 0, slot_info.key_length);
+ os_free_memory(key);
+ }
+
+ return os_err;
+}
+
+static os_error_code sah_handle_sk_slot_read(uint32_t info)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+ scc_slot_t slot_info;
+ os_error_code os_err;
+ uint8_t *key = NULL;
+
+ os_err = os_copy_from_user(&slot_info, (void *)info, sizeof(slot_info));
+
+ if (os_err == OS_ERROR_OK_S) {
+
+ /* This operation is not allowed for user keys */
+ slot_info.code = FSL_RETURN_NO_RESOURCE_S;
+ os_err =
+ os_copy_to_user((void *)info, &slot_info,
+ sizeof(slot_info));
+
+ return os_err;
+ }
+
+ if (os_err == OS_ERROR_OK_S) {
+ /* Allow slop in alloc in case we are rounding up to word multiple */
+ key = os_alloc_memory(slot_info.key_length + 3, GFP_KERNEL);
+ if (key == NULL) {
+ ret = FSL_RETURN_NO_RESOURCE_S;
+ os_err = OS_ERROR_NO_MEMORY_S;
+ }
+ }
+
+ if (os_err == OS_ERROR_OK_S) {
+ unsigned key_length = slot_info.key_length;
+
+ /* @bug Do some PERMISSIONS checking - make sure this is SW key */
+
+ /* Round up if necessary, as SCC call wants a multiple of 32-bit
+ * values for the full object being loaded. */
+ if ((key_length & 3) != 0) {
+ key_length += 4 - (key_length & 3);
+ }
+ ret = keystore_slot_read(&system_keystore,
+ slot_info.ownerid, slot_info.slot,
+ key_length, key);
+
+ /* @bug do some error checking */
+
+ /* Send key back to user */
+ os_err = os_copy_to_user(slot_info.key, key,
+ slot_info.key_length);
+
+ slot_info.code = ret;
+ os_err =
+ os_copy_to_user((void *)info, &slot_info,
+ sizeof(slot_info));
+ }
+
+ if (key != NULL) {
+ memset(key, 0, slot_info.key_length);
+ os_free_memory(key);
+ }
+
+ return os_err;
+}
+
+static os_error_code sah_handle_sk_slot_encrypt(uint32_t info)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+ scc_slot_t slot_info;
+ os_error_code os_err;
+ scc_return_t scc_ret;
+ uint8_t *key = NULL;
+
+ os_err = os_copy_from_user(&slot_info, (void *)info, sizeof(slot_info));
+
+ if (os_err == OS_ERROR_OK_S) {
+ key = os_alloc_memory(slot_info.key_length, GFP_KERNEL);
+ if (key == NULL) {
+ ret = FSL_RETURN_NO_RESOURCE_S;
+ }
+ }
+
+ if (key != NULL) {
+
+ scc_ret = keystore_slot_encrypt(NULL, &system_keystore,
+ slot_info.ownerid,
+ slot_info.slot,
+ slot_info.key_length, key);
+
+ if (scc_ret != SCC_RET_OK) {
+ ret = FSL_RETURN_ERROR_S;
+ } else {
+ os_err =
+ os_copy_to_user(slot_info.key, key,
+ slot_info.key_length);
+ if (os_err != OS_ERROR_OK_S) {
+ ret = FSL_RETURN_INTERNAL_ERROR_S;
+ } else {
+ ret = FSL_RETURN_OK_S;
+ }
+ }
+
+ slot_info.code = ret;
+ os_err =
+ os_copy_to_user((void *)info, &slot_info,
+ sizeof(slot_info));
+
+ memset(key, 0, slot_info.key_length);
+ os_free_memory(key);
+ }
+
+ return os_err;
+}
+
+static os_error_code sah_handle_sk_slot_decrypt(uint32_t info)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+ scc_slot_t slot_info; /*!< decrypt request fields */
+ os_error_code os_err;
+ scc_return_t scc_ret;
+ uint8_t *key = NULL;
+
+ os_err = os_copy_from_user(&slot_info, (void *)info, sizeof(slot_info));
+
+ if (os_err == OS_ERROR_OK_S) {
+ key = os_alloc_memory(slot_info.key_length, GFP_KERNEL);
+ if (key == NULL) {
+ ret = FSL_RETURN_NO_RESOURCE_S;
+ os_err = OS_ERROR_OK_S;
+ } else {
+ os_err = os_copy_from_user(key, slot_info.key,
+ slot_info.key_length);
+ }
+ }
+
+ if (os_err == OS_ERROR_OK_S) {
+ scc_ret = keystore_slot_decrypt(NULL, &system_keystore,
+ slot_info.ownerid,
+ slot_info.slot,
+ slot_info.key_length, key);
+ if (scc_ret == SCC_RET_OK) {
+ ret = FSL_RETURN_OK_S;
+ } else {
+ ret = FSL_RETURN_ERROR_S;
+ }
+
+ slot_info.code = ret;
+ os_err =
+ os_copy_to_user((void *)info, &slot_info,
+ sizeof(slot_info));
+ }
+
+ if (key != NULL) {
+ memset(key, 0, slot_info.key_length);
+ os_free_memory(key);
+ }
+
+ return os_err;
+}
+
+/*!
+ * Register a user
+ *
+ * @brief Register a user
+ *
+ * @param user_ctx information about this user
+ *
+ * @return status code
+ */
+fsl_shw_return_t sah_handle_registration(fsl_shw_uco_t * user_ctx)
+{
+ /* Initialize the user's result pool (like sah_Queue_Construct() */
+ user_ctx->result_pool.head = NULL;
+ user_ctx->result_pool.tail = NULL;
+ user_ctx->result_pool.count = 0;
+
+ /* initialize the user's partition chain */
+ user_ctx->partition = NULL;
+
+ return FSL_RETURN_OK_S;
+}
+
+/*!
+ * Deregister a user
+ *
+ * @brief Deregister a user
+ *
+ * @param user_ctx information about this user
+ *
+ * @return status code
+ */
+fsl_shw_return_t sah_handle_deregistration(fsl_shw_uco_t * user_ctx)
+{
+ /* NOTE:
+ * This will release any secure partitions that are held by the user.
+ * Encryption keys that were placed in the system keystore by the user
+ * should not be removed here, because they might have been shared with
+ * another process. The user must be careful to release any that are no
+ * longer in use.
+ */
+ fsl_shw_return_t ret = FSL_RETURN_OK_S;
+
+#ifdef FSL_HAVE_SCC2
+ fsl_shw_spo_t *partition;
+ struct mm_struct *mm = current->mm;
+
+ while ((user_ctx->partition != NULL) && (ret == FSL_RETURN_OK_S)) {
+
+ partition = user_ctx->partition;
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS
+ ("Found an abandoned secure partition at %p, releasing",
+ partition);
+#endif
+
+ /* It appears that current->mm is not valid if this is called from a
+ * close routine (perhaps only if the program raised an exception that
+ * caused it to close?) If that is the case, then still free the
+ * partition, but do not remove it from the memory space (dangerous?)
+ */
+
+ if (mm == NULL) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG
+ ("Warning: no mm structure found, not unmapping "
+ "partition from user memory\n");
+#endif
+ } else {
+ /* Unmap the memory region (see sys_munmap in mmap.c) */
+ /* Note that this assumes a single memory partition */
+ unmap_user_memory(partition->user_base, 8192);
+ }
+
+ /* If the memory was successfully released */
+ if (ret == OS_ERROR_OK_S) {
+ /* release the partition */
+ scc_release_partition(partition->kernel_base);
+
+ /* and remove it from the users context */
+ deregister_user_partition(user_ctx,
+ partition->user_base);
+
+ ret = FSL_RETURN_OK_S;
+ } else {
+ ret = FSL_RETURN_ERROR_S;
+
+ goto out;
+ }
+ }
+ out:
+#endif /* FSL_HAVE_SCC2 */
+
+ return ret;
+}
+
+/*!
+ * Sets up memory to extract results from results pool
+ *
+ * @brief Sets up memory to extract results from results pool
+ *
+ * @param user_ctx information about this user
+ * @param[in,out] arg contains input parameters and fields that the driver
+ * fills in
+ *
+ * @return os error code or 0 on success
+ */
+int sah_get_results_pointers(fsl_shw_uco_t * user_ctx, uint32_t arg)
+{
+ sah_results results_arg; /* kernel mode usable version of 'arg' */
+ fsl_shw_result_t *user_results; /* user mode address of results */
+ unsigned *user_actual; /* user mode address of actual number of results */
+ unsigned actual; /* local memory of actual number of results */
+ int ret_val = OS_ERROR_FAIL_S;
+ sah_Head_Desc *finished_request;
+ unsigned int loop;
+
+ /* copy structure from user to kernel space */
+ if (!os_copy_from_user(&results_arg, (void *)arg, sizeof(sah_results))) {
+ /* save user space pointers */
+ user_actual = results_arg.actual; /* where count goes */
+ user_results = results_arg.results; /* where results goe */
+
+ /* Set pointer for actual value to temporary kernel memory location */
+ results_arg.actual = &actual;
+
+ /* Allocate kernel memory to hold temporary copy of the results */
+ results_arg.results =
+ os_alloc_memory(sizeof(fsl_shw_result_t) *
+ results_arg.requested, GFP_KERNEL);
+
+ /* if memory allocated, continue */
+ if (results_arg.results == NULL) {
+ ret_val = OS_ERROR_NO_MEMORY_S;
+ } else {
+ fsl_shw_return_t get_status;
+
+ /* get the results */
+ get_status =
+ sah_get_results_from_pool(user_ctx, &results_arg);
+
+ /* free the copy of the user space descriptor chain */
+ for (loop = 0; loop < actual; ++loop) {
+ /* get sah_Head_Desc from results and put user address into
+ * the return structure */
+ finished_request =
+ results_arg.results[loop].user_desc;
+ results_arg.results[loop].user_desc =
+ finished_request->user_desc;
+ /* return the descriptor chain memory to the block free pool */
+ sah_Free_Chained_Descriptors(finished_request);
+ }
+
+ /* if no errors, copy results and then the actual number of results
+ * back to user space
+ */
+ if (get_status == FSL_RETURN_OK_S) {
+ if (os_copy_to_user
+ (user_results, results_arg.results,
+ actual * sizeof(fsl_shw_result_t))
+ || os_copy_to_user(user_actual, &actual,
+ sizeof(user_actual))) {
+ ret_val = OS_ERROR_FAIL_S;
+ } else {
+ ret_val = 0; /* no error */
+ }
+ }
+ /* free the allocated memory */
+ os_free_memory(results_arg.results);
+ }
+ }
+
+ return ret_val;
+}
+
+/*!
+ * Extracts results from results pool
+ *
+ * @brief Extract results from results pool
+ *
+ * @param user_ctx information about this user
+ * @param[in,out] arg contains input parameters and fields that the
+ * driver fills in
+ *
+ * @return status code
+ */
+fsl_shw_return_t sah_get_results_from_pool(volatile fsl_shw_uco_t * user_ctx,
+ sah_results * arg)
+{
+ sah_Head_Desc *finished_request;
+ unsigned int loop = 0;
+ os_lock_context_t int_flags;
+
+ /* Get the number of results requested, up to total number of results
+ * available
+ */
+ do {
+ /* Protect state of user's result pool until we have retrieved and
+ * remove the first entry, or determined that the pool is empty. */
+ os_lock_save_context(desc_queue_lock, int_flags);
+ finished_request = user_ctx->result_pool.head;
+
+ if (finished_request != NULL) {
+ sah_Queue_Remove_Entry((sah_Queue *) & user_ctx->
+ result_pool);
+ os_unlock_restore_context(desc_queue_lock, int_flags);
+
+ /* Prepare to free. */
+ (void)sah_DePhysicalise_Descriptors(finished_request);
+
+ arg->results[loop].user_ref =
+ finished_request->user_ref;
+ arg->results[loop].code = finished_request->result;
+ arg->results[loop].detail1 =
+ finished_request->fault_address;
+ arg->results[loop].detail2 = 0;
+ arg->results[loop].user_desc = finished_request;
+
+ loop++;
+ } else { /* finished_request is NULL */
+ /* pool is empty */
+ os_unlock_restore_context(desc_queue_lock, int_flags);
+ }
+
+ } while ((loop < arg->requested) && (finished_request != NULL));
+
+ /* record number of results actually obtained */
+ *arg->actual = loop;
+
+ return FSL_RETURN_OK_S;
+}
+
+/*!
+ * Converts descriptor chain to kernel space (from user space) and submits
+ * chain to Sahara for processing
+ *
+ * @brief Submits converted descriptor chain to sahara
+ *
+ * @param user_ctx Pointer to Kernel version of user's ctx
+ * @param user_space_desc user space address of descriptor chain that is
+ * in user space
+ *
+ * @return OS status code
+ */
+static int handle_sah_ioctl_dar(fsl_shw_uco_t * user_ctx,
+ uint32_t user_space_desc)
+{
+ int os_error_code = OS_ERROR_FAIL_S;
+ sah_Head_Desc *desc_chain_head; /* chain in kernel - virtual address */
+
+ /* This will re-create the linked list so that the SAHARA hardware can
+ * DMA on it.
+ */
+ desc_chain_head = sah_Copy_Descriptors(user_ctx,
+ (sah_Head_Desc *)
+ user_space_desc);
+
+ if (desc_chain_head == NULL) {
+ /* We may have failed due to a -EFAULT as well, but we will return
+ * OS_ERROR_NO_MEMORY_S since either way it is a memory related
+ * failure.
+ */
+ os_error_code = OS_ERROR_NO_MEMORY_S;
+ } else {
+ fsl_shw_return_t stat;
+
+ desc_chain_head->user_info = user_ctx;
+ desc_chain_head->user_desc = (sah_Head_Desc *) user_space_desc;
+
+ if (desc_chain_head->uco_flags & FSL_UCO_BLOCKING_MODE) {
+#ifdef SAHARA_POLL_MODE
+ sah_Handle_Poll(desc_chain_head);
+#else
+ sah_blocking_mode(desc_chain_head);
+#endif
+ stat = desc_chain_head->result;
+ /* return the descriptor chain memory to the block free pool */
+ sah_Free_Chained_Descriptors(desc_chain_head);
+ /* Tell user how the call turned out */
+
+ /* Copy 'result' back up to the result member.
+ *
+ * The dereference of the different member will cause correct the
+ * arithmetic to occur on the user-space address because of the
+ * missing dma/bus locations in the user mode version of the
+ * sah_Desc structure. */
+ os_error_code =
+ os_copy_to_user((void *)(user_space_desc
+ + offsetof(sah_Head_Desc,
+ uco_flags)),
+ &stat, sizeof(fsl_shw_return_t));
+
+ } else { /* not blocking mode - queue and forget */
+
+ if (desc_chain_head->uco_flags & FSL_UCO_CALLBACK_MODE) {
+ user_ctx->process = os_get_process_handle();
+ user_ctx->callback = sah_user_callback;
+ }
+#ifdef SAHARA_POLL_MODE
+ /* will put results in result pool */
+ sah_Handle_Poll(desc_chain_head);
+#else
+ /* just put someting in the DAR */
+ sah_Queue_Manager_Append_Entry(desc_chain_head);
+#endif
+ /* assume all went well */
+ os_error_code = OS_ERROR_OK_S;
+ }
+ }
+
+ return os_error_code;
+}
+
+static void sah_user_callback(fsl_shw_uco_t * user_ctx)
+{
+ os_send_signal(user_ctx->process, SIGUSR2);
+}
+
+/*!
+ * This function is called when a thread attempts to read from the /proc/sahara
+ * file. Upon read, statistics and information about the state of the driver
+ * are returned in nthe supplied buffer.
+ *
+ * @brief SAHARA PROCFS read function.
+ *
+ * @param buf Anything written to this buffer will be returned to the
+ * user-space process that is reading from this proc entry.
+ * @param start Part of the kernel prototype.
+ * @param offset Part of the kernel prototype.
+ * @param count The size of the buf argument.
+ * @param eof An integer which is set to one to tell the user-space
+ * process that there is no more data to read.
+ * @param data Part of the kernel prototype.
+ *
+ * @return The number of bytes written to the proc entry.
+ */
+#if !defined(CONFIG_DEVFS_FS) || (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static int sah_read_procfs(char *buf,
+ char **start,
+ off_t offset, int count, int *eof, void *data)
+{
+ int output_bytes = 0;
+ int in_queue_count = 0;
+ os_lock_context_t lock_context;
+
+ os_lock_save_context(desc_queue_lock, lock_context);
+ in_queue_count = sah_Queue_Manager_Count_Entries(TRUE, 0);
+ os_unlock_restore_context(desc_queue_lock, lock_context);
+ output_bytes += snprintf(buf, count - output_bytes, "queued: %d\n",
+ in_queue_count);
+ output_bytes += snprintf(buf + output_bytes, count - output_bytes,
+ "Descriptors: %d, "
+ "Interrupts %d (%d Done1Done2, %d Done1Busy2, "
+ " %d Done1)\n",
+ dar_count, interrupt_count, done1done2_count,
+ done1busy2_count, done1_count);
+ output_bytes += snprintf(buf + output_bytes, count - output_bytes,
+ "Control: %08x\n", sah_HW_Read_Control());
+#if !defined(FSL_HAVE_SAHARA4) || defined(SAHARA4_NO_USE_SQUIB)
+ output_bytes += snprintf(buf + output_bytes, count - output_bytes,
+ "IDAR: %08x; CDAR: %08x\n",
+ sah_HW_Read_IDAR(), sah_HW_Read_CDAR());
+#endif
+#ifdef DIAG_DRV_STATUS
+ output_bytes += snprintf(buf + output_bytes, count - output_bytes,
+ "Status: %08x; Error Status: %08x; Op Status: %08x\n",
+ sah_HW_Read_Status(),
+ sah_HW_Read_Error_Status(),
+ sah_HW_Read_Op_Status());
+#endif
+#ifdef FSL_HAVE_SAHARA4
+ output_bytes += snprintf(buf + output_bytes, count - output_bytes,
+ "MMStat: %08x; Config: %08x\n",
+ sah_HW_Read_MM_Status(), sah_HW_Read_Config());
+#endif
+
+ /* Signal the end of the file */
+ *eof = 1;
+
+ /* To get rid of the unused parameter warnings */
+ (void)start;
+ (void)data;
+ (void)offset;
+
+ return output_bytes;
+}
+
+static int sah_write_procfs(struct file *file, const char __user * buffer,
+ unsigned long count, void *data)
+{
+
+ /* Any write to this file will reset all counts. */
+ dar_count = interrupt_count = done1done2_count =
+ done1busy2_count = done1_count = 0;
+
+ (void)file;
+ (void)buffer;
+ (void)data;
+
+ return count;
+}
+
+#endif
+
+#ifndef SAHARA_POLL_MODE
+/*!
+ * Block user call until processing is complete.
+ *
+ * @param entry The user's request.
+ *
+ * @return An OS error code, or 0 if no error
+ */
+int sah_blocking_mode(sah_Head_Desc * entry)
+{
+ int os_error_code = 0;
+ sah_Queue_Status status;
+
+ /* queue entry, put something in the DAR, if nothing is there currently */
+ sah_Queue_Manager_Append_Entry(entry);
+
+ /* get this descriptor chain's current status */
+ status = ((volatile sah_Head_Desc *)entry)->status;
+
+ while (!SAH_DESC_PROCESSED(status)) {
+ extern sah_Queue *main_queue;
+
+ DEFINE_WAIT(sahara_wait); /* create a wait queue entry. Linux */
+
+ /* enter the wait queue entry into the queue */
+ prepare_to_wait(&Wait_queue, &sahara_wait, TASK_INTERRUPTIBLE);
+
+ /* check if this entry has been processed */
+ status = ((volatile sah_Head_Desc *)entry)->status;
+
+ if (!SAH_DESC_PROCESSED(status)) {
+ /* go to sleep - Linux */
+ schedule();
+ }
+
+ /* un-queue the 'prepare to wait' queue? - Linux */
+ finish_wait(&Wait_queue, &sahara_wait);
+
+ /* signal belongs to this thread? */
+ if (signal_pending(current)) { /* Linux */
+ os_lock_context_t lock_flags;
+
+ /* don't allow access during this check and operation */
+ os_lock_save_context(desc_queue_lock, lock_flags);
+ status = ((volatile sah_Head_Desc *)entry)->status;
+ if (status == SAH_STATE_PENDING) {
+ sah_Queue_Remove_Any_Entry(main_queue, entry);
+ entry->result = FSL_RETURN_INTERNAL_ERROR_S;
+ ((volatile sah_Head_Desc *)entry)->status =
+ SAH_STATE_FAILED;
+ }
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+ }
+
+ status = ((volatile sah_Head_Desc *)entry)->status;
+ } /* while ... */
+
+ /* Do this so that caller can free */
+ (void)sah_DePhysicalise_Descriptors(entry);
+
+ return os_error_code;
+}
+
+/*!
+ * If interrupt does not return in a reasonable time, time out, trigger
+ * interrupt, and continue with process
+ *
+ * @param data ignored
+ */
+void sahara_timeout_handler(unsigned long data)
+{
+ /* Sahara has not issuing an interrupt, so timed out */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Sahara HW did not respond. Resetting.\n");
+#endif
+ /* assume hardware needs resetting */
+ sah_Handle_Interrupt(SAH_EXEC_FAULT);
+ /* wake up sleeping thread to try again */
+ wake_up_interruptible(&Wait_queue);
+}
+
+#endif /* ifndef SAHARA_POLL_MODE */
+
+/* End of sah_driver_interface.c */
diff --git a/drivers/mxc/security/sahara2/sah_hardware_interface.c b/drivers/mxc/security/sahara2/sah_hardware_interface.c
new file mode 100644
index 000000000000..ff3cb987dc7f
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_hardware_interface.c
@@ -0,0 +1,808 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file sah_hardware_interface.c
+ *
+ * @brief Provides an interface to the SAHARA hardware registers.
+ *
+ */
+
+/* SAHARA Includes */
+#include <sah_driver_common.h>
+#include <sah_hardware_interface.h>
+#include <sah_memory_mapper.h>
+#include <sah_kernel.h>
+
+#if defined DIAG_DRV_IF || defined(DO_DBG)
+#include <diagnostic.h>
+#ifndef LOG_KDIAG
+#define LOG_KDIAG(x) os_printk("%s\n", x)
+#endif
+
+static void sah_Dump_Link(const char *prefix, const sah_Link * link,
+ dma_addr_t addr);
+
+/* This is for sprintf() to use when constructing output. */
+#define DIAG_MSG_SIZE 1024
+/* was 200 */
+#define MAX_DUMP 200
+static char Diag_msg[DIAG_MSG_SIZE];
+
+#endif /* DIAG_DRV_IF */
+
+/*!
+ * Number of descriptors sent to Sahara. This value should only be updated
+ * with the main queue lock held.
+ */
+uint32_t dar_count;
+
+/* sahara virtual base address */
+void *sah_virt_base;
+
+/*! The "link-list optimize" bit in the Header of a Descriptor */
+#define SAH_HDR_LLO 0x01000000
+
+#define SAHARA_VERSION_REGISTER_OFFSET 0x000
+#define SAHARA_DAR_REGISTER_OFFSET 0x004
+#define SAHARA_CONTROL_REGISTER_OFFSET 0x008
+#define SAHARA_COMMAND_REGISTER_OFFSET 0x00C
+#define SAHARA_STATUS_REGISTER_OFFSET 0x010
+#define SAHARA_ESTATUS_REGISTER_OFFSET 0x014
+#define SAHARA_FLT_ADD_REGISTER_OFFSET 0x018
+#define SAHARA_CDAR_REGISTER_OFFSET 0x01C
+#define SAHARA_IDAR_REGISTER_OFFSET 0x020
+#define SAHARA_OSTATUS_REGISTER_OFFSET 0x028
+#define SAHARA_CONFIG_REGISTER_OFFSET 0x02C
+#define SAHARA_MM_STAT_REGISTER_OFFSET 0x030
+
+
+/* Local Functions */
+#if defined DIAG_DRV_IF || defined DO_DBG
+void sah_Dump_Region(const char *prefix, const unsigned char *data,
+ dma_addr_t addr, unsigned length);
+
+#endif /* DIAG_DRV_IF */
+
+/* time out value when polling SAHARA status register for completion */
+static uint32_t sah_poll_timeout = 0xFFFFFFFF;
+
+/*!
+ * Polls Sahara to determine when its current operation is complete
+ *
+ * @return last value found in Sahara's status register
+ */
+sah_Execute_Status sah_Wait_On_Sahara()
+{
+ uint32_t count = 0; /* ensure we don't get stuck in the loop forever */
+ sah_Execute_Status status; /* Sahara's status register */
+ uint32_t stat_reg;
+
+ pr_debug("Entered sah_Wait_On_Sahara\n");
+
+ do {
+ /* get current status register from Sahara */
+ stat_reg = sah_HW_Read_Status();
+ status = stat_reg & SAH_EXEC_STATE_MASK;
+
+ /* timeout if SAHARA takes too long to complete */
+ if (++count == sah_poll_timeout) {
+ status = SAH_EXEC_FAULT;
+ printk("sah_Wait_On_Sahara timed out\n");
+ }
+
+ /* stay in loop as long as Sahara is still busy */
+ } while ((status == SAH_EXEC_BUSY) || (status == SAH_EXEC_DONE1_BUSY2));
+
+ if (status == SAH_EXEC_ERROR1) {
+ if (stat_reg & OP_STATUS) {
+ status = SAH_EXEC_OPSTAT1;
+ }
+ }
+
+ return status;
+} /* sah_Wait_on_Sahara() */
+
+/*!
+ * This function resets the SAHARA hardware. The following operations are
+ * performed:
+ * 1. Resets SAHARA.
+ * 2. Requests BATCH mode.
+ * 3. Enables interrupts.
+ * 4. Requests Little Endian mode.
+ *
+ * @brief SAHARA hardware reset function.
+ *
+ * @return void
+ */
+int sah_HW_Reset(void)
+{
+ sah_Execute_Status sah_state;
+ int status; /* this is the value to return to the calling routine */
+ uint32_t saha_control = 0;
+
+#ifndef USE_3WORD_BURST
+#ifdef FSL_HAVE_SAHARA2
+ saha_control |= (8 << 16); /* Allow 8-word burst */
+#endif
+#else
+/***************** HARDWARE BUG WORK AROUND ******************/
+/* A burst size of > 4 can cause Sahara DMA to issue invalid AHB transactions
+ * when crossing 1KB boundaries. By limiting the 'burst size' to 3, these
+ * invalid transactions will not be generated, but Sahara will still transfer
+ * data more efficiently than if the burst size were set to 1.
+ */
+ saha_control |= (3 << 16); /* Limit DMA burst size. For versions 2/3 */
+#endif /* USE_3WORD_BURST */
+
+#ifdef DIAG_DRV_IF
+ snprintf(Diag_msg, DIAG_MSG_SIZE,
+ "Address of sah_virt_base = 0x%08x\n",
+ sah_virt_base);
+ LOG_KDIAG(Diag_msg);
+ snprintf(Diag_msg, DIAG_MSG_SIZE,
+ "Sahara Status register before reset: %08x",
+ sah_HW_Read_Status());
+ LOG_KDIAG(Diag_msg);
+#endif
+
+ /* Write the Reset & BATCH mode command to the SAHARA Command register. */
+ sah_HW_Write_Command(CMD_BATCH | CMD_RESET);
+#ifdef SAHARA4_NO_USE_SQUIB
+ {
+ uint32_t cfg = sah_HW_Read_Config();
+ cfg &= ~0x10000;
+ sah_HW_Write_Config(cfg);
+ }
+#endif
+
+ sah_poll_timeout = 0x0FFFFFFF;
+ sah_state = sah_Wait_On_Sahara();
+#ifdef DIAG_DRV_IF
+ snprintf(Diag_msg, DIAG_MSG_SIZE,
+ "Sahara Status register after reset: %08x",
+ sah_HW_Read_Status());
+ LOG_KDIAG(Diag_msg);
+#endif
+ /* on reset completion, check that Sahara is in the idle state */
+ status = (sah_state == SAH_EXEC_IDLE) ? 0 : OS_ERROR_FAIL_S;
+
+ /* Set initial value out of reset */
+ sah_HW_Write_Control(saha_control);
+
+#ifndef NO_RESEED_WORKAROUND
+/***************** HARDWARE BUG WORK AROUND ******************/
+/* In order to set the 'auto reseed' bit, must first acquire a random value. */
+ /*
+ * to solve a hardware bug, a random number must be generated before
+ * the 'RNG Auto Reseed' bit can be set. So this generates a random
+ * number that is thrown away.
+ *
+ * Note that the interrupt bit has not been set at this point so
+ * the result can be polled.
+ */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Create and submit Random Number Descriptor");
+#endif
+
+ if (status == OS_ERROR_OK_S) {
+ /* place to put random number */
+ volatile uint32_t *random_data_ptr;
+ sah_Head_Desc *random_desc;
+ dma_addr_t desc_dma;
+ dma_addr_t rand_dma;
+ const int rnd_cnt = 3; /* how many random 32-bit values to get */
+
+ /* Get space for data -- assume at least 32-bit aligned! */
+ random_data_ptr = os_alloc_memory(rnd_cnt * sizeof(uint32_t),
+ GFP_ATOMIC);
+
+ random_desc = sah_Alloc_Head_Descriptor();
+
+ if ((random_data_ptr == NULL) || (random_desc == NULL)) {
+ status = OS_ERROR_FAIL_S;
+ } else {
+ int i;
+
+ /* Clear out values */
+ for (i = 0; i < rnd_cnt; i++) {
+ random_data_ptr[i] = 0;
+ }
+
+ rand_dma = os_pa(random_data_ptr);
+
+ random_desc->desc.header = 0xB18C0000; /* LLO get random number */
+ random_desc->desc.len1 =
+ rnd_cnt * sizeof(*random_data_ptr);
+ random_desc->desc.ptr1 = (void *)rand_dma;
+ random_desc->desc.original_ptr1 =
+ (void *)random_data_ptr;
+
+ random_desc->desc.len2 = 0; /* not used */
+ random_desc->desc.ptr2 = 0; /* not used */
+
+ random_desc->desc.next = 0; /* chain terminates here */
+ random_desc->desc.original_next = 0; /* chain terminates here */
+
+ desc_dma = random_desc->desc.dma_addr;
+
+ /* Force in-cache data out to RAM */
+ os_cache_clean_range(random_data_ptr,
+ rnd_cnt *
+ sizeof(*random_data_ptr));
+
+ /* pass descriptor to Sahara */
+ sah_HW_Write_DAR(desc_dma);
+
+ /*
+ * Wait for RNG to complete (interrupts are disabled at this point
+ * due to sahara being reset previously) then check for error
+ */
+ sah_state = sah_Wait_On_Sahara();
+ /* Force CPU to ignore in-cache and reload from RAM */
+ os_cache_inv_range(random_data_ptr,
+ rnd_cnt * sizeof(*random_data_ptr));
+
+ /* if it didn't move to done state, an error occured */
+ if (
+#ifndef SUBMIT_MULTIPLE_DARS
+ (sah_state != SAH_EXEC_IDLE) &&
+#endif
+ (sah_state != SAH_EXEC_DONE1)
+ ) {
+ status = OS_ERROR_FAIL_S;
+ os_printk
+ ("(sahara) Failure: state is %08x; random_data is"
+ " %08x\n", sah_state, *random_data_ptr);
+ os_printk
+ ("(sahara) CDAR: %08x, IDAR: %08x, FADR: %08x,"
+ " ESTAT: %08x\n", sah_HW_Read_CDAR(),
+ sah_HW_Read_IDAR(),
+ sah_HW_Read_Fault_Address(),
+ sah_HW_Read_Error_Status());
+ } else {
+ int i;
+ int seen_rand = 0;
+
+ for (i = 0; i < rnd_cnt; i++) {
+ if (*random_data_ptr != 0) {
+ seen_rand = 1;
+ break;
+ }
+ }
+ if (!seen_rand) {
+ status = OS_ERROR_FAIL_S;
+ os_printk
+ ("(sahara) Error: Random number is zero!\n");
+ }
+ }
+ }
+
+ if (random_data_ptr) {
+ os_free_memory((void *)random_data_ptr);
+ }
+ if (random_desc) {
+ sah_Free_Head_Descriptor(random_desc);
+ }
+ }
+/***************** END HARDWARE BUG WORK AROUND ******************/
+#endif
+
+ if (status == 0) {
+#ifdef FSL_HAVE_SAHARA2
+ saha_control |= CTRL_RNG_RESEED;
+#endif
+
+#ifndef SAHARA_POLL_MODE
+ saha_control |= CTRL_INT_EN; /* enable interrupts */
+#else
+ sah_poll_timeout = SAHARA_POLL_MODE_TIMEOUT;
+#endif
+
+#ifdef DIAG_DRV_IF
+ snprintf(Diag_msg, DIAG_MSG_SIZE,
+ "Setting up Sahara's Control Register: %08x\n",
+ saha_control);
+ LOG_KDIAG(Diag_msg);
+#endif
+
+ /* Rewrite the setup to the SAHARA Control register */
+ sah_HW_Write_Control(saha_control);
+#ifdef DIAG_DRV_IF
+ snprintf(Diag_msg, DIAG_MSG_SIZE,
+ "Sahara Status register after control write: %08x",
+ sah_HW_Read_Status());
+ LOG_KDIAG(Diag_msg);
+#endif
+
+#ifdef FSL_HAVE_SAHARA4
+ {
+ uint32_t cfg = sah_HW_Read_Config();
+ sah_HW_Write_Config(cfg | 0x100); /* Add RNG auto-reseed */
+ }
+#endif
+ } else {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Reset failed\n");
+#endif
+ }
+
+ return status;
+} /* sah_HW_Reset() */
+
+/*!
+ * This function enables High Assurance mode.
+ *
+ * @brief SAHARA hardware enable High Assurance mode.
+ *
+ * @return FSL_RETURN_OK_S - if HA was set successfully
+ * @return FSL_RETURN_INTERNAL_ERROR_S - if HA was not set due to SAHARA
+ * being busy.
+ */
+fsl_shw_return_t sah_HW_Set_HA(void)
+{
+ /* This is the value to write to the register */
+ uint32_t value;
+
+ /* Read from the control register. */
+ value = sah_HW_Read_Control();
+
+ /* Set the HA bit */
+ value |= CTRL_HA;
+
+ /* Write to the control register. */
+ sah_HW_Write_Control(value);
+
+ /* Read from the control register. */
+ value = sah_HW_Read_Control();
+
+ return (value & CTRL_HA) ? FSL_RETURN_OK_S :
+ FSL_RETURN_INTERNAL_ERROR_S;
+}
+
+/*!
+ * This function reads the SAHARA hardware Version Register.
+ *
+ * @brief Read SAHARA hardware Version Register.
+ *
+ * @return uint32_t Register value.
+ */
+uint32_t sah_HW_Read_Version(void)
+{
+ return os_read32(sah_virt_base + SAHARA_VERSION_REGISTER_OFFSET);
+}
+
+/*!
+ * This function reads the SAHARA hardware Control Register.
+ *
+ * @brief Read SAHARA hardware Control Register.
+ *
+ * @return uint32_t Register value.
+ */
+uint32_t sah_HW_Read_Control(void)
+{
+ return os_read32(sah_virt_base + SAHARA_CONTROL_REGISTER_OFFSET);
+}
+
+/*!
+ * This function reads the SAHARA hardware Status Register.
+ *
+ * @brief Read SAHARA hardware Status Register.
+ *
+ * @return uint32_t Register value.
+ */
+uint32_t sah_HW_Read_Status(void)
+{
+ return os_read32(sah_virt_base + SAHARA_STATUS_REGISTER_OFFSET);
+}
+
+/*!
+ * This function reads the SAHARA hardware Error Status Register.
+ *
+ * @brief Read SAHARA hardware Error Status Register.
+ *
+ * @return uint32_t Error Status value.
+ */
+uint32_t sah_HW_Read_Error_Status(void)
+{
+ return os_read32(sah_virt_base + SAHARA_ESTATUS_REGISTER_OFFSET);
+}
+
+/*!
+ * This function reads the SAHARA hardware Op Status Register.
+ *
+ * @brief Read SAHARA hardware Op Status Register.
+ *
+ * @return uint32_t Op Status value.
+ */
+uint32_t sah_HW_Read_Op_Status(void)
+{
+ return os_read32(sah_virt_base + SAHARA_OSTATUS_REGISTER_OFFSET);
+}
+
+/*!
+ * This function reads the SAHARA hardware Descriptor Address Register.
+ *
+ * @brief Read SAHARA hardware DAR Register.
+ *
+ * @return uint32_t DAR value.
+ */
+uint32_t sah_HW_Read_DAR(void)
+{
+ return os_read32(sah_virt_base + SAHARA_DAR_REGISTER_OFFSET);
+}
+
+/*!
+ * This function reads the SAHARA hardware Current Descriptor Address Register.
+ *
+ * @brief Read SAHARA hardware CDAR Register.
+ *
+ * @return uint32_t CDAR value.
+ */
+uint32_t sah_HW_Read_CDAR(void)
+{
+ return os_read32(sah_virt_base + SAHARA_CDAR_REGISTER_OFFSET);
+}
+
+/*!
+ * This function reads the SAHARA hardware Initial Descriptor Address Register.
+ *
+ * @brief Read SAHARA hardware IDAR Register.
+ *
+ * @return uint32_t IDAR value.
+ */
+uint32_t sah_HW_Read_IDAR(void)
+{
+ return os_read32(sah_virt_base + SAHARA_IDAR_REGISTER_OFFSET);
+}
+
+/*!
+ * This function reads the SAHARA hardware Fault Address Register.
+ *
+ * @brief Read SAHARA Fault Address Register.
+ *
+ * @return uint32_t Fault Address value.
+ */
+uint32_t sah_HW_Read_Fault_Address(void)
+{
+ return os_read32(sah_virt_base + SAHARA_FLT_ADD_REGISTER_OFFSET);
+}
+
+/*!
+ * This function reads the SAHARA hardware Multiple Master Status Register.
+ *
+ * @brief Read SAHARA hardware MM Stat Register.
+ *
+ * @return uint32_t MM Stat value.
+ */
+uint32_t sah_HW_Read_MM_Status(void)
+{
+ return os_read32(sah_virt_base + SAHARA_MM_STAT_REGISTER_OFFSET);
+}
+
+/*!
+ * This function reads the SAHARA hardware Configuration Register.
+ *
+ * @brief Read SAHARA Configuration Register.
+ *
+ * @return uint32_t Configuration value.
+ */
+uint32_t sah_HW_Read_Config(void)
+{
+ return os_read32(sah_virt_base + SAHARA_CONFIG_REGISTER_OFFSET);
+}
+
+/*!
+ * This function writes a command to the SAHARA hardware Command Register.
+ *
+ * @brief Write to SAHARA hardware Command Register.
+ *
+ * @param command An unsigned 32bit command value.
+ *
+ * @return void
+ */
+void sah_HW_Write_Command(uint32_t command)
+{
+ os_write32(sah_virt_base + SAHARA_COMMAND_REGISTER_OFFSET, command);
+}
+
+/*!
+ * This function writes a control value to the SAHARA hardware Control
+ * Register.
+ *
+ * @brief Write to SAHARA hardware Control Register.
+ *
+ * @param control An unsigned 32bit control value.
+ *
+ * @return void
+ */
+void sah_HW_Write_Control(uint32_t control)
+{
+ os_write32(sah_virt_base + SAHARA_CONTROL_REGISTER_OFFSET, control);
+}
+
+/*!
+ * This function writes a configuration value to the SAHARA hardware Configuration
+ * Register.
+ *
+ * @brief Write to SAHARA hardware Configuration Register.
+ *
+ * @param configuration An unsigned 32bit configuration value.
+ *
+ * @return void
+ */
+void sah_HW_Write_Config(uint32_t configuration)
+{
+ os_write32(sah_virt_base + SAHARA_CONFIG_REGISTER_OFFSET,
+ configuration);
+}
+
+/*!
+ * This function writes a descriptor address to the SAHARA Descriptor Address
+ * Register.
+ *
+ * @brief Write to SAHARA Descriptor Address Register.
+ *
+ * @param pointer An unsigned 32bit descriptor address value.
+ *
+ * @return void
+ */
+void sah_HW_Write_DAR(uint32_t pointer)
+{
+ os_write32(sah_virt_base + SAHARA_DAR_REGISTER_OFFSET, pointer);
+ dar_count++;
+}
+
+#if defined DIAG_DRV_IF || defined DO_DBG
+
+static char *interpret_header(uint32_t header)
+{
+ unsigned desc_type = ((header >> 24) & 0x70) | ((header >> 16) & 0xF);
+
+ switch (desc_type) {
+ case 0x12:
+ return "5/SKHA_ST_CTX";
+ case 0x13:
+ return "35/SKHA_LD_MODE_KEY";
+ case 0x14:
+ return "38/SKHA_LD_MODE_IN_CPHR_ST_CTX";
+ case 0x15:
+ return "4/SKHA_IN_CPHR_OUT";
+ case 0x16:
+ return "34/SKHA_ST_SBOX";
+ case 0x18:
+ return "1/SKHA_LD_MODE_IV_KEY";
+ case 0x19:
+ return "33/SKHA_ST_SBOX";
+ case 0x1D:
+ return "2/SKHA_LD_MODE_IN_CPHR_OUT";
+ case 0x22:
+ return "11/MDHA_ST_MD";
+ case 0x25:
+ return "10/MDHA_HASH_ST_MD";
+ case 0x28:
+ return "6/MDHA_LD_MODE_MD_KEY";
+ case 0x2A:
+ return "39/MDHA_ICV";
+ case 0x2D:
+ return "8/MDHA_LD_MODE_HASH_ST_MD";
+ case 0x3C:
+ return "18/RNG_GEN";
+ case 0x40:
+ return "19/PKHA_LD_N_E";
+ case 0x41:
+ return "36/PKHA_LD_A3_B0";
+ case 0x42:
+ return "27/PKHA_ST_A_B";
+ case 0x43:
+ return "22/PKHA_LD_A_B";
+ case 0x44:
+ return "23/PKHA_LD_A0_A1";
+ case 0x45:
+ return "24/PKHA_LD_A2_A3";
+ case 0x46:
+ return "25/PKHA_LD_B0_B1";
+ case 0x47:
+ return "26/PKHA_LD_B2_B3";
+ case 0x48:
+ return "28/PKHA_ST_A0_A1";
+ case 0x49:
+ return "29/PKHA_ST_A2_A3";
+ case 0x4A:
+ return "30/PKHA_ST_B0_B1";
+ case 0x4B:
+ return "31/PKHA_ST_B2_B3";
+ case 0x4C:
+ return "32/PKHA_EX_ST_B1";
+ case 0x4D:
+ return "20/PKHA_LD_A_EX_ST_B";
+ case 0x4E:
+ return "21/PKHA_LD_N_EX_ST_B";
+ case 0x4F:
+ return "37/PKHA_ST_B1_B2";
+ default:
+ return "??/UNKNOWN";
+ }
+} /* cvt_desc_name() */
+
+/*!
+ * Dump chain of descriptors to the log.
+ *
+ * @brief Dump descriptor chain
+ *
+ * @param chain Kernel virtual address of start of chain of descriptors
+ *
+ * @return void
+ */
+void sah_Dump_Chain(const sah_Desc * chain, dma_addr_t addr)
+{
+ int desc_no = 1;
+
+ pr_debug("Chain for Sahara\n");
+
+ while (chain != NULL) {
+ char desc_name[50];
+
+ sprintf(desc_name, "Desc %02d (%s)\n" KERN_DEBUG "Desc ",
+ desc_no++, interpret_header(chain->header));
+
+ sah_Dump_Words(desc_name, (unsigned *)chain, addr,
+ 6 /* #words in h/w link */ );
+ if (chain->original_ptr1) {
+ if (chain->header & SAH_HDR_LLO) {
+ sah_Dump_Region(" Data1",
+ (unsigned char *)chain->
+ original_ptr1,
+ (dma_addr_t) chain->ptr1,
+ chain->len1);
+ } else {
+ sah_Dump_Link(" Link1", chain->original_ptr1,
+ (dma_addr_t) chain->ptr1);
+ }
+ }
+ if (chain->ptr2) {
+ if (chain->header & SAH_HDR_LLO) {
+ sah_Dump_Region(" Data2",
+ (unsigned char *)chain->
+ original_ptr2,
+ (dma_addr_t) chain->ptr2,
+ chain->len2);
+ } else {
+ sah_Dump_Link(" Link2", chain->original_ptr2,
+ (dma_addr_t) chain->ptr2);
+ }
+ }
+
+ addr = (dma_addr_t) chain->next;
+ chain = (chain->next) ? (chain->original_next) : NULL;
+ }
+}
+
+/*!
+ * Dump chain of links to the log.
+ *
+ * @brief Dump chain of links
+ *
+ * @param prefix Text to put in front of dumped data
+ * @param link Kernel virtual address of start of chain of links
+ *
+ * @return void
+ */
+static void sah_Dump_Link(const char *prefix, const sah_Link * link,
+ dma_addr_t addr)
+{
+#ifdef DUMP_SCC_DATA
+ extern uint8_t *sahara_partition_base;
+ extern dma_addr_t sahara_partition_phys;
+#endif
+
+ while (link != NULL) {
+ sah_Dump_Words(prefix, (unsigned *)link, addr,
+ 3 /* # words in h/w link */ );
+ if (link->flags & SAH_STORED_KEY_INFO) {
+#ifdef SAH_DUMP_DATA
+#ifdef DUMP_SCC_DATA
+ sah_Dump_Region(" Data",
+ (uint8_t *) link->data -
+ (uint8_t *) sahara_partition_phys +
+ sahara_partition_base,
+ (dma_addr_t) link->data, link->len);
+#else
+ pr_debug(" Key Slot %d\n", link->slot);
+#endif
+#endif
+ } else {
+#ifdef SAH_DUMP_DATA
+ sah_Dump_Region(" Data", link->original_data,
+ (dma_addr_t) link->data, link->len);
+#endif
+ }
+ addr = (dma_addr_t) link->next;
+ link = link->original_next;
+ }
+}
+
+/*!
+ * Dump given region of data to the log.
+ *
+ * @brief Dump data
+ *
+ * @param prefix Text to put in front of dumped data
+ * @param data Kernel virtual address of start of region to dump
+ * @param length Amount of data to dump
+ *
+ * @return void
+ */
+void sah_Dump_Region(const char *prefix, const unsigned char *data,
+ dma_addr_t addr, unsigned length)
+{
+ unsigned count;
+ char *output;
+ unsigned data_len;
+
+ sprintf(Diag_msg, "%s (%08X,%u):", prefix, addr, length);
+
+ /* Restrict amount of data to dump */
+ if (length > MAX_DUMP) {
+ data_len = MAX_DUMP;
+ } else {
+ data_len = length;
+ }
+
+ /* We've already printed some text in output buffer, skip over it */
+ output = Diag_msg + strlen(Diag_msg);
+
+ for (count = 0; count < data_len; count++) {
+ if ((count % 4) == 0) {
+ *output++ = ' ';
+ }
+ sprintf(output, "%02X", *data++);
+ output += 2;
+ }
+
+ pr_debug("%s\n", Diag_msg);
+}
+
+/*!
+ * Dump given word of data to the log.
+ *
+ * @brief Dump data
+ *
+ * @param prefix Text to put in front of dumped data
+ * @param data Kernel virtual address of start of region to dump
+ * @param word_count Amount of data to dump
+ *
+ * @return void
+ */
+void sah_Dump_Words(const char *prefix, const unsigned *data, dma_addr_t addr,
+ unsigned word_count)
+{
+ char *output;
+
+ sprintf(Diag_msg, "%s (%08X,%uw): ", prefix, addr, word_count);
+
+ /* We've already printed some text in output buffer, skip over it */
+ output = Diag_msg + strlen(Diag_msg);
+
+ while (word_count--) {
+ sprintf(output, "%08X ", *data++);
+ output += 9;
+ }
+
+ pr_debug("%s\n", Diag_msg);
+
+}
+
+#endif /* DIAG_DRV_IF */
+
+/* End of sah_hardware_interface.c */
diff --git a/drivers/mxc/security/sahara2/sah_interrupt_handler.c b/drivers/mxc/security/sahara2/sah_interrupt_handler.c
new file mode 100644
index 000000000000..959c3d8cf7bb
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_interrupt_handler.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+* @file sah_interrupt_handler.c
+*
+* @brief Provides a hardware interrupt handling mechanism for device driver.
+*
+* This file needs to be ported for a non-Linux OS.
+*
+* It gets a call at #sah_Intr_Init() during initialization.
+*
+* #sah_Intr_Top_Half() is intended to be the Interrupt Service Routine. It
+* calls a portable function in another file to process the Sahara status.
+*
+* #sah_Intr_Bottom_Half() is a 'background' task scheduled by the top half to
+* take care of the expensive tasks of the interrupt processing.
+*
+* The driver shutdown code calls #sah_Intr_Release().
+*
+*/
+
+#include <portable_os.h>
+
+/* SAHARA Includes */
+#include <sah_kernel.h>
+#include <sah_interrupt_handler.h>
+#include <sah_status_manager.h>
+#include <sah_hardware_interface.h>
+#include <sah_queue_manager.h>
+
+/*Enable this flag for debugging*/
+#if 0
+#define DIAG_DRV_INTERRUPT
+#endif
+
+#ifdef DIAG_DRV_INTERRUPT
+#include <diagnostic.h>
+#endif
+
+/*!
+ * Number of interrupts received. This value should only be updated during
+ * interrupt processing.
+ */
+uint32_t interrupt_count;
+
+#ifndef SAHARA_POLL_MODE
+
+#if !defined(LINUX_VERSION_CODE) || LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+#define irqreturn_t void
+#define IRQ_HANDLED
+#define IRQ_RETVAL(x)
+#endif
+
+/* Internal Prototypes */
+static irqreturn_t sah_Intr_Top_Half(int irq, void *dev_id);
+
+#ifdef KERNEL_TEST
+extern void (*SAHARA_INT_PTR) (int, void *);
+#endif
+
+unsigned long reset_flag;
+static void sah_Intr_Bottom_Half(unsigned long reset_flag);
+
+/* This is the Bottom Half Task, (reset flag set to false) */
+DECLARE_TASKLET(BH_task, sah_Intr_Bottom_Half, (unsigned long)&reset_flag);
+
+/*! This is set by the Initialisation function */
+wait_queue_head_t *int_queue = NULL;
+
+/*!
+*******************************************************************************
+* This function registers the Top Half of the interrupt handler with the Kernel
+* and the SAHARA IRQ number.
+*
+* @brief SAHARA Interrupt Handler Initialisation
+*
+* @param wait_queue Pointer to the wait queue used by driver interface
+*
+* @return int A return of Zero indicates successful initialisation.
+*/
+/******************************************************************************
+*
+* CAUTION: NONE
+*
+* MODIFICATION HISTORY:
+*
+* Date Person Change
+* 30/07/2003 MW Initial Creation
+******************************************************************************/
+int sah_Intr_Init(wait_queue_head_t * wait_queue)
+{
+
+#ifdef DIAG_DRV_INTERRUPT
+ char err_string[200];
+#endif
+
+ int result;
+
+#ifdef KERNEL_TEST
+ SAHARA_INT_PTR = sah_Intr_Top_Half;
+#endif
+
+ /* Set queue used by the interrupt handler to match the driver interface */
+ int_queue = wait_queue;
+
+ /* Request use of the Interrupt line. */
+ result = request_irq(SAHARA_IRQ,
+ sah_Intr_Top_Half, 0, SAHARA_NAME, NULL);
+
+#ifdef DIAG_DRV_INTERRUPT
+ if (result != 0) {
+ sprintf(err_string, "Cannot use SAHARA interrupt line %d. "
+ "request_irq() return code is %i.", SAHARA_IRQ, result);
+ LOG_KDIAG(err_string);
+ } else {
+ sprintf(err_string,
+ "SAHARA driver registered for interrupt %d. ",
+ SAHARA_IRQ);
+ LOG_KDIAG(err_string);
+ }
+#endif
+
+ return result;
+}
+
+/*!
+*******************************************************************************
+* This function releases the Top Half of the interrupt handler. The driver will
+* not receive any more interrupts after calling this functions.
+*
+* @brief SAHARA Interrupt Handler Release
+*
+* @return void
+*/
+/******************************************************************************
+*
+* CAUTION: NONE
+*
+* MODIFICATION HISTORY:
+*
+* Date Person Change
+* 30/07/2003 MW Initial Creation
+******************************************************************************/
+void sah_Intr_Release(void)
+{
+ /* Release the Interrupt. */
+ free_irq(SAHARA_IRQ, NULL);
+}
+
+/*!
+*******************************************************************************
+* This function is the Top Half of the interrupt handler. It updates the
+* status of any finished descriptor chains and then tries to add any pending
+* requests into the hardware. It then queues the bottom half to complete
+* operations on the finished chains.
+*
+* @brief SAHARA Interrupt Handler Top Half
+*
+* @param irq Part of the kernel prototype.
+* @param dev_id Part of the kernel prototype.
+*
+* @return An IRQ_RETVAL() -- non-zero to that function means 'handled'
+*/
+static irqreturn_t sah_Intr_Top_Half(int irq, void *dev_id)
+{
+#if defined(DIAG_DRV_INTERRUPT) && defined(DIAG_DURING_INTERRUPT)
+ LOG_KDIAG("Top half of Sahara's interrupt handler called.");
+#endif
+
+ interrupt_count++;
+ reset_flag = sah_Handle_Interrupt(sah_HW_Read_Status());
+
+ /* Schedule the Bottom Half of the Interrupt. */
+ tasklet_schedule(&BH_task);
+
+ /* To get rid of the unused parameter warnings. */
+ irq = 0;
+ dev_id = NULL;
+ return IRQ_RETVAL(1);
+}
+
+/*!
+*******************************************************************************
+* This function is the Bottom Half of the interrupt handler. It calls
+* #sah_postprocess_queue() to complete the processing of the Descriptor Chains
+* which were finished by the hardware.
+*
+* @brief SAHARA Interrupt Handler Bottom Half
+*
+* @param data Part of the kernel prototype.
+*
+* @return void
+*/
+static void sah_Intr_Bottom_Half(unsigned long reset_flag)
+{
+#if defined(DIAG_DRV_INTERRUPT) && defined(DIAG_DURING_INTERRUPT)
+ LOG_KDIAG("Bottom half of Sahara's interrupt handler called.");
+#endif
+ sah_postprocess_queue(*(unsigned long *)reset_flag);
+
+ return;
+}
+
+/* end of sah_interrupt_handler.c */
+#endif /* ifndef SAHARA_POLL_MODE */
diff --git a/drivers/mxc/security/sahara2/sah_memory_mapper.c b/drivers/mxc/security/sahara2/sah_memory_mapper.c
new file mode 100644
index 000000000000..2e3b80b14873
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_memory_mapper.c
@@ -0,0 +1,2356 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+* @file sah_memory_mapper.c
+*
+* @brief Re-creates SAHARA data structures in Kernel memory such that they are
+* suitable for DMA. Provides support for kernel API.
+*
+* This file needs to be ported.
+*
+* The memory mapper gets a call at #sah_Init_Mem_Map() during driver
+* initialization.
+*
+* The routine #sah_Copy_Descriptors() is used to bring descriptor chains from
+* user memory down to kernel memory, relink using physical addresses, and make
+* sure that all user data will be accessible by the Sahara DMA.
+* #sah_Destroy_Descriptors() does the inverse.
+*
+* The #sah_Alloc_Block(), #sah_Free_Block(), and #sah_Block_Add_Page() routines
+* implement a cache of free blocks used when allocating descriptors and links
+* within the kernel.
+*
+* The memory mapper gets a call at #sah_Stop_Mem_Map() during driver shutdown.
+*
+*/
+
+#include <sah_driver_common.h>
+#include <sah_kernel.h>
+#include <sah_queue_manager.h>
+#include <sah_memory_mapper.h>
+#ifdef FSL_HAVE_SCC2
+#include <linux/mxc_scc2_driver.h>
+#else
+#include <linux/mxc_scc_driver.h>
+#endif
+
+#if defined(DIAG_DRV_IF) || defined(DIAG_MEM) || defined(DO_DBG)
+#include <diagnostic.h>
+#include <sah_hardware_interface.h>
+#endif
+
+#include <linux/mm.h> /* get_user_pages() */
+#include <linux/pagemap.h>
+#include <linux/dmapool.h>
+
+#include <linux/slab.h>
+#include <linux/highmem.h>
+
+#if defined(DIAG_MEM) || defined(DIAG_DRV_IF)
+#define DIAG_MSG_SIZE 1024
+static char Diag_msg[DIAG_MSG_SIZE];
+#endif
+
+#ifdef LINUX_VERSION_CODE
+#define FLUSH_SPECIFIC_DATA_ONLY
+#else
+#define SELF_MANAGED_POOL
+#endif
+
+#if defined(LINUX_VERSION_CODE)
+EXPORT_SYMBOL(sah_Alloc_Link);
+EXPORT_SYMBOL(sah_Free_Link);
+EXPORT_SYMBOL(sah_Alloc_Descriptor);
+EXPORT_SYMBOL(sah_Free_Descriptor);
+EXPORT_SYMBOL(sah_Alloc_Head_Descriptor);
+EXPORT_SYMBOL(sah_Free_Head_Descriptor);
+EXPORT_SYMBOL(sah_Physicalise_Descriptors);
+EXPORT_SYMBOL(sah_DePhysicalise_Descriptors);
+#endif
+
+/* Determine if L2 cache support should be built in. */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21))
+#ifdef CONFIG_OUTER_CACHE
+#define HAS_L2_CACHE
+#endif
+#else
+#ifdef CONFIG_CPU_CACHE_L210
+#define HAS_L2_CACHE
+#endif
+#endif
+
+/* Number of bytes the hardware uses out of sah_Link and sah_*Desc structs */
+#define SAH_HW_LINK_LEN 1
+#define SAH_HW_DESC_LEN 24
+
+/* Macros for Descriptors */
+#define SAH_LLO_BIT 0x01000000
+#define sah_Desc_Get_LLO(desc) (desc->header & SAH_LLO_BIT)
+#define sah_Desc_Set_Header(desc, h) (desc->header = (h))
+
+#define sah_Desc_Get_Next(desc) (desc->next)
+#define sah_Desc_Set_Next(desc, n) (desc->next = (n))
+
+#define sah_Desc_Get_Ptr1(desc) (desc->ptr1)
+#define sah_Desc_Get_Ptr2(desc) (desc->ptr2)
+#define sah_Desc_Set_Ptr1(desc,p1) (desc->ptr1 = (p1))
+#define sah_Desc_Set_Ptr2(desc,p2) (desc->ptr2 = (p2))
+
+#define sah_Desc_Get_Len1(desc) (desc->len1)
+#define sah_Desc_Get_Len2(desc) (desc->len2)
+#define sah_Desc_Set_Len1(desc,l1) (desc->len1 = (l1))
+#define sah_Desc_Set_Len2(desc,l2) (desc->len2 = (l2))
+
+/* Macros for Links */
+#define sah_Link_Get_Next(link) (link->next)
+#define sah_Link_Set_Next(link, n) (link->next = (n))
+
+#define sah_Link_Get_Data(link) (link->data)
+#define sah_Link_Set_Data(link,d) (link->data = (d))
+
+#define sah_Link_Get_Len(link) (link->len)
+#define sah_Link_Set_Len(link, l) (link->len = (l))
+
+#define sah_Link_Get_Flags(link) (link->flags)
+
+/* Memory block defines */
+/* Warning! This assumes that kernel version of sah_Link
+ * is larger than kernel version of sah_Desc.
+ */
+#define MEM_BLOCK_SIZE sizeof(sah_Link)
+
+/*! Structure for link/descriptor memory blocks in internal pool */
+typedef struct mem_block {
+ uint8_t data[MEM_BLOCK_SIZE]; /*!< the actual buffer area */
+ struct mem_block *next; /*!< next block when in free chain */
+ dma_addr_t dma_addr; /*!< physical address of @a data */
+} Mem_Block;
+
+#define MEM_BLOCK_ENTRIES (PAGE_SIZE / sizeof(Mem_Block))
+
+#define MEM_BIG_BLOCK_SIZE sizeof(sah_Head_Desc)
+
+/*! Structure for head descriptor memory blocks in internal pool */
+typedef struct mem_big_block {
+ uint8_t data[MEM_BIG_BLOCK_SIZE]; /*!< the actual buffer area */
+ struct mem_big_block *next; /*!< next block when in free chain */
+ uint32_t dma_addr; /*!< physical address of @a data */
+} Mem_Big_Block;
+
+#define MEM_BIG_BLOCK_ENTRIES (PAGE_SIZE / sizeof(Mem_Big_Block))
+
+/* Shared variables */
+
+/*!
+ * Lock to protect the memory chain composed of #block_free_head and
+ * #block_free_tail.
+ */
+static os_lock_t mem_lock;
+
+#ifndef SELF_MANAGED_POOL
+static struct dma_pool *big_dma_pool = NULL;
+static struct dma_pool *small_dma_pool = NULL;
+#endif
+
+#ifdef SELF_MANAGED_POOL
+/*!
+ * Memory block free pool - pointer to first block. Chain is protected by
+ * #mem_lock.
+ */
+static Mem_Block *block_free_head = NULL;
+/*!
+ * Memory block free pool - pointer to last block. Chain is protected by
+ * #mem_lock.
+ */
+static Mem_Block *block_free_tail = NULL;
+/*!
+ * Memory block free pool - pointer to first block. Chain is protected by
+ * #mem_lock.
+ */
+static Mem_Big_Block *big_block_free_head = NULL;
+/*!
+ * Memory block free pool - pointer to last block. Chain is protected by
+ * #mem_lock.
+a */
+static Mem_Big_Block *big_block_free_tail = NULL;
+#endif /* SELF_MANAGED_POOL */
+
+static Mem_Block *sah_Alloc_Block(void);
+static void sah_Free_Block(Mem_Block * block);
+static Mem_Big_Block *sah_Alloc_Big_Block(void);
+static void sah_Free_Big_Block(Mem_Big_Block * block);
+#ifdef SELF_MANAGED_POOL
+static void sah_Append_Block(Mem_Block * block);
+static void sah_Append_Big_Block(Mem_Big_Block * block);
+#endif /* SELF_MANAGED_POOL */
+
+/* Page context structure. Used by wire_user_memory and unwire_user_memory */
+typedef struct page_ctx_t {
+ uint32_t count;
+ struct page **local_pages;
+} page_ctx_t;
+
+/*!
+*******************************************************************************
+* Map and wire down a region of user memory.
+*
+*
+* @param address Userspace address of the memory to wire
+* @param length Length of the memory region to wire
+* @param page_ctx Page context, to be passed to unwire_user_memory
+*
+* @return (if successful) Kernel virtual address of the wired pages
+*/
+void *wire_user_memory(void *address, uint32_t length, void **page_ctx)
+{
+ void *kernel_black_addr = NULL;
+ int result = -1;
+ int page_index = 0;
+ page_ctx_t *page_context;
+ int nr_pages = 0;
+ unsigned long start_page;
+ fsl_shw_return_t status;
+
+ /* Determine the number of pages being used for this link */
+ nr_pages = (((unsigned long)(address) & ~PAGE_MASK)
+ + length + ~PAGE_MASK) >> PAGE_SHIFT;
+
+ start_page = (unsigned long)(address) & PAGE_MASK;
+
+ /* Allocate some memory to keep track of the wired user pages, so that
+ * they can be deallocated later. The block of memory will contain both
+ * the structure and the array of pages.
+ */
+ page_context = kmalloc(sizeof(page_ctx_t)
+ + nr_pages * sizeof(struct page *), GFP_KERNEL);
+
+ if (page_context == NULL) {
+ status = FSL_RETURN_NO_RESOURCE_S; /* no memory! */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("kmalloc() failed.");
+#endif
+ return NULL;
+ }
+
+ /* Set the page pointer to point to the allocated region of memory */
+ page_context->local_pages = (void *)page_context + sizeof(page_ctx_t);
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS("page_context at: %p, local_pages at: %p",
+ (void *)page_context,
+ (void *)(page_context->local_pages));
+#endif
+
+ /* Wire down the pages from user space */
+ down_read(&current->mm->mmap_sem);
+ result = get_user_pages(current, current->mm,
+ start_page, nr_pages, WRITE, 0 /* noforce */ ,
+ (page_context->local_pages), NULL);
+ up_read(&current->mm->mmap_sem);
+
+ if (result < nr_pages) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("get_user_pages() failed.");
+#endif
+ if (result > 0) {
+ for (page_index = 0; page_index < result; page_index++) {
+ page_cache_release((page_context->
+ local_pages[page_index]));
+ }
+
+ kfree(page_context);
+ }
+ return NULL;
+ }
+
+ kernel_black_addr = page_address(page_context->local_pages[0]) +
+ ((unsigned long)address & ~PAGE_MASK);
+
+ page_context->count = nr_pages;
+ *page_ctx = page_context;
+
+ return kernel_black_addr;
+}
+
+/*!
+*******************************************************************************
+* Release and unmap a region of user memory.
+*
+* @param page_ctx Page context from wire_user_memory
+*/
+void unwire_user_memory(void **page_ctx)
+{
+ int page_index = 0;
+ struct page_ctx_t *page_context = *page_ctx;
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS("page_context at: %p, first page at:%p, count: %i",
+ (void *)page_context,
+ (void *)(page_context->local_pages),
+ page_context->count);
+#endif
+
+ if ((page_context != NULL) && (page_context->local_pages != NULL)) {
+ for (page_index = 0; page_index < page_context->count;
+ page_index++) {
+ page_cache_release(page_context->
+ local_pages[page_index]);
+ }
+
+ kfree(page_context);
+ *page_ctx = NULL;
+ }
+}
+
+/*!
+*******************************************************************************
+* Map some physical memory into a users memory space
+*
+* @param vma Memory structure to map to
+* @param physical_addr Physical address of the memory to be mapped in
+* @param size Size of the memory to map (bytes)
+*
+* @return
+*/
+os_error_code
+map_user_memory(struct vm_area_struct *vma, uint32_t physical_addr,
+ uint32_t size)
+{
+ os_error_code retval;
+
+ /* Map the acquired partition into the user's memory space */
+ vma->vm_end = vma->vm_start + size;
+
+ /* set cache policy to uncached so that each write of the UMID and
+ * permissions get directly to the SCC2 in order to engage it
+ * properly. Once the permissions have been written, it may be
+ * useful to provide a service for the user to request a different
+ * cache policy
+ */
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ /* Make sure that the user cannot fork() a child which will inherit
+ * this mapping, as it creates a security hole. Likewise, do not
+ * allow the user to 'expand' his mapping beyond this partition.
+ */
+ vma->vm_flags |= VM_IO | VM_RESERVED | VM_DONTCOPY | VM_DONTEXPAND;
+
+ retval = remap_pfn_range(vma,
+ vma->vm_start,
+ __phys_to_pfn(physical_addr),
+ size, vma->vm_page_prot);
+
+ return retval;
+}
+
+/*!
+*******************************************************************************
+* Remove some memory from a user's memory space
+*
+* @param user_addr Userspace address of the memory to be unmapped
+* @param size Size of the memory to map (bytes)
+*
+* @return
+*/
+os_error_code unmap_user_memory(uint32_t user_addr, uint32_t size)
+{
+ os_error_code retval;
+ struct mm_struct *mm = current->mm;
+
+ /* Unmap the memory region (see sys_munmap in mmap.c) */
+ down_write(&mm->mmap_sem);
+ retval = do_munmap(mm, (unsigned long)user_addr, size);
+ up_write(&mm->mmap_sem);
+
+ return retval;
+}
+
+/*!
+*******************************************************************************
+* Free descriptor back to free pool
+*
+* @brief Free descriptor
+*
+* @param desc A descriptor allocated with sah_Alloc_Descriptor().
+*
+* @return none
+*
+*/
+void sah_Free_Descriptor(sah_Desc * desc)
+{
+ memset(desc, 0x45, sizeof(*desc));
+ sah_Free_Block((Mem_Block *) desc);
+}
+
+/*!
+*******************************************************************************
+* Free Head descriptor back to free pool
+*
+* @brief Free Head descriptor
+*
+* @param desc A Head descriptor allocated with sah_Alloc_Head_Descriptor().
+*
+* @return none
+*
+*/
+void sah_Free_Head_Descriptor(sah_Head_Desc * desc)
+{
+ memset(desc, 0x43, sizeof(*desc));
+ sah_Free_Big_Block((Mem_Big_Block *) desc);
+}
+
+/*!
+*******************************************************************************
+* Free link back to free pool
+*
+* @brief Free link
+*
+* @param link A link allocated with sah_Alloc_Link().
+*
+* @return none
+*
+*/
+void sah_Free_Link(sah_Link * link)
+{
+ memset(link, 0x41, sizeof(*link));
+ sah_Free_Block((Mem_Block *) link);
+}
+
+/*!
+*******************************************************************************
+* This function runs through a descriptor chain pointed to by a user-space
+* address. It duplicates each descriptor in Kernel space memory and calls
+* sah_Copy_Links() to handle any links attached to the descriptors. This
+* function cleans-up everything that it created in the case of a failure.
+*
+* @brief Kernel Descriptor Chain Copier
+*
+* @param fsl_shw_uco_t The user context to act under
+* @param user_head_desc A Head Descriptor pointer from user-space.
+*
+* @return sah_Head_Desc * - A virtual address of the first descriptor in the
+* chain.
+* @return NULL - If there was some error.
+*
+*/
+sah_Head_Desc *sah_Copy_Descriptors(fsl_shw_uco_t * user_ctx,
+ sah_Head_Desc * user_head_desc)
+{
+ sah_Desc *curr_desc = NULL;
+ sah_Desc *prev_desc = NULL;
+ sah_Desc *next_desc = NULL;
+ sah_Head_Desc *head_desc = NULL;
+ sah_Desc *user_desc = NULL;
+ unsigned long result;
+
+ /* Internal status variable to be used in this function */
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Head_Desc *ret_val = NULL;
+
+ /* This will be set to True when we have finished processing our
+ * descriptor chain.
+ */
+ int drv_if_done = FALSE;
+ int is_this_the_head = TRUE;
+
+ do {
+ /* Allocate memory for this descriptor */
+ if (is_this_the_head) {
+ head_desc =
+ (sah_Head_Desc *) sah_Alloc_Head_Descriptor();
+
+#ifdef DIAG_MEM
+ sprintf(Diag_msg,
+ "Alloc_Head_Descriptor returned %p\n",
+ head_desc);
+ LOG_KDIAG(Diag_msg);
+#endif
+ if (head_desc == NULL) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG
+ ("sah_Alloc_Head_Descriptor() failed.");
+#endif
+ drv_if_done = TRUE;
+ status = FSL_RETURN_NO_RESOURCE_S;
+ } else {
+ void *virt_addr = head_desc->desc.virt_addr;
+ dma_addr_t dma_addr = head_desc->desc.dma_addr;
+
+ /* Copy the head descriptor from user-space */
+ /* Instead of copying the whole structure,
+ * unneeded bits at the end are left off.
+ * The user space version is missing virt/dma addrs, which
+ * means that the copy will be off for flags... */
+ result = copy_from_user(head_desc,
+ user_head_desc,
+ (sizeof(*head_desc) -
+ sizeof(head_desc->desc.
+ dma_addr) -
+ sizeof(head_desc->desc.
+ virt_addr) -
+ sizeof(head_desc->desc.
+ original_ptr1) -
+/* sizeof(head_desc->desc.original_ptr2) -
+ sizeof(head_desc->status) -
+ sizeof(head_desc->error_status) -
+ sizeof(head_desc->fault_address) -
+ sizeof(head_desc->current_dar) -
+ sizeof(head_desc->result) -
+ sizeof(head_desc->next) -
+ sizeof(head_desc->prev) -
+ sizeof(head_desc->user_desc) -
+*/ sizeof(head_desc->out1_ptr) -
+ sizeof(head_desc->
+ out2_ptr) -
+ sizeof(head_desc->
+ out_len)));
+ /* there really isn't a 'next' descriptor at this point, so
+ * set that pointer to NULL, but remember it for if/when there
+ * is a next */
+ next_desc = head_desc->desc.next;
+ head_desc->desc.next = NULL;
+
+ if (result != 0) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("copy_from_user() failed.");
+#endif
+ drv_if_done = TRUE;
+ status = FSL_RETURN_INTERNAL_ERROR_S;
+ /* when destroying the descriptor, skip these links.
+ * They've not been copied down, so don't exist */
+ head_desc->desc.ptr1 = NULL;
+ head_desc->desc.ptr2 = NULL;
+
+ } else {
+ /* The kernel DESC has five more words than user DESC, so
+ * the missing values are in the middle of the HEAD DESC,
+ * causing values after the missing ones to be at different
+ * offsets in kernel and user space.
+ *
+ * Patch up the problem by moving field two spots.
+ * This assumes sizeof(pointer) == sizeof(uint32_t).
+ * Note that 'user_info' is not needed, so not copied.
+ */
+ head_desc->user_ref =
+ (uint32_t) head_desc->desc.dma_addr;
+ head_desc->uco_flags =
+ (uint32_t) head_desc->desc.
+ original_ptr1;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS(
+ "User flags: %x; User Reference: %x",
+ head_desc->uco_flags,
+ head_desc->user_ref);
+#endif
+ /* These values were destroyed by the copy. */
+ head_desc->desc.virt_addr = virt_addr;
+ head_desc->desc.dma_addr = dma_addr;
+
+ /* ensure that the save descriptor chain bit is not set.
+ * the copy of the user space descriptor chain should
+ * always be deleted */
+ head_desc->uco_flags &=
+ ~FSL_UCO_SAVE_DESC_CHAIN;
+
+ curr_desc = (sah_Desc *) head_desc;
+ is_this_the_head = FALSE;
+ }
+ }
+ } else { /* not head */
+ curr_desc = sah_Alloc_Descriptor();
+#ifdef DIAG_MEM
+ LOG_KDIAG_ARGS("Alloc_Descriptor returned %p\n",
+ curr_desc);
+#endif
+ if (curr_desc == NULL) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("sah_Alloc_Descriptor() failed.");
+#endif
+ drv_if_done = TRUE;
+ status = FSL_RETURN_NO_RESOURCE_S;
+ } else {
+ /* need to update the previous descriptors' next field to
+ * pointer to the current descriptor. */
+ prev_desc->original_next = curr_desc;
+ prev_desc->next =
+ (sah_Desc *) curr_desc->dma_addr;
+
+ /* Copy the current descriptor from user-space */
+ /* The virtual address and DMA address part of the sah_Desc
+ * struct are not copied to user space */
+ result = copy_from_user(curr_desc, user_desc, (sizeof(sah_Desc) - sizeof(dma_addr_t) - /* dma_addr */
+ sizeof(uint32_t) - /* virt_addr */
+ sizeof(void *) - /* original_ptr1 */
+ sizeof(void *) - /* original_ptr2 */
+ sizeof(sah_Desc **))); /* original_next */
+ /* there really isn't a 'next' descriptor at this point, so
+ * set that pointer to NULL, but remember it for if/when there
+ * is a next */
+ next_desc = curr_desc->next;
+ curr_desc->next = NULL;
+ curr_desc->original_next = NULL;
+
+ if (result != 0) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("copy_from_user() failed.");
+#endif
+ drv_if_done = TRUE;
+ status = FSL_RETURN_INTERNAL_ERROR_S;
+ /* when destroying the descriptor chain, skip these links.
+ * They've not been copied down, so don't exist */
+ curr_desc->ptr1 = NULL;
+ curr_desc->ptr2 = NULL;
+ }
+ }
+ } /* end if (is_this_the_head) */
+
+ if (status == FSL_RETURN_OK_S) {
+ if (!(curr_desc->header & SAH_LLO_BIT)) {
+ /* One or both pointer fields being NULL is a valid
+ * configuration. */
+ if (curr_desc->ptr1 == NULL) {
+ curr_desc->original_ptr1 = NULL;
+ } else {
+ /* pointer fields point to sah_Link structures */
+ curr_desc->original_ptr1 =
+ sah_Copy_Links(user_ctx, curr_desc->ptr1);
+ if (curr_desc->original_ptr1 == NULL) {
+ /* This descriptor and any links created successfully
+ * are cleaned-up at the bottom of this function. */
+ drv_if_done = TRUE;
+ status =
+ FSL_RETURN_INTERNAL_ERROR_S;
+ /* mark that link 2 doesn't exist */
+ curr_desc->ptr2 = NULL;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG
+ ("sah_Copy_Links() failed.");
+#endif
+ } else {
+ curr_desc->ptr1 = (void *)
+ ((sah_Link *) curr_desc->
+ original_ptr1)->dma_addr;
+ }
+ }
+
+ if (status == FSL_RETURN_OK_S) {
+ if (curr_desc->ptr2 == NULL) {
+ curr_desc->original_ptr2 = NULL;
+ } else {
+ /* pointer fields point to sah_Link structures */
+ curr_desc->original_ptr2 =
+ sah_Copy_Links(user_ctx, curr_desc->ptr2);
+ if (curr_desc->original_ptr2 ==
+ NULL) {
+ /* This descriptor and any links created
+ * successfully are cleaned-up at the bottom of
+ * this function. */
+ drv_if_done = TRUE;
+ status =
+ FSL_RETURN_INTERNAL_ERROR_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG
+ ("sah_Copy_Links() failed.");
+#endif
+ } else {
+ curr_desc->ptr2 =
+ (void
+ *)(((sah_Link *)
+ curr_desc->
+ original_ptr2)
+ ->dma_addr);
+ }
+ }
+ }
+ } else {
+ /* Pointer fields point directly to user buffers. We don't
+ * support this mode.
+ */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG
+ ("The LLO bit in the Descriptor Header field was "
+ "set. This an invalid configuration.");
+#endif
+ drv_if_done = TRUE;
+ status = FSL_RETURN_INTERNAL_ERROR_S;
+ }
+ }
+
+ if (status == FSL_RETURN_OK_S) {
+ user_desc = next_desc;
+ prev_desc = curr_desc;
+ if (user_desc == NULL) {
+ /* We have reached the end our our descriptor chain */
+ drv_if_done = TRUE;
+ }
+ }
+
+ } while (drv_if_done == FALSE);
+
+ if (status != FSL_RETURN_OK_S) {
+ /* Clean-up if failed */
+ if (head_desc != NULL) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Error! Calling destroy descriptors!\n");
+#endif
+ sah_Destroy_Descriptors(head_desc);
+ }
+ ret_val = NULL;
+ } else {
+ /* Flush the caches */
+#ifndef FLUSH_SPECIFIC_DATA_ONLY
+ os_flush_cache_all();
+#endif
+
+ /* Success. Return the DMA'able head descriptor. */
+ ret_val = head_desc;
+
+ }
+
+ return ret_val;
+} /* sah_Copy_Descriptors() */
+
+/*!
+*******************************************************************************
+* This function runs through a sah_Link chain pointed to by a kernel-space
+* address. It computes the physical address for each pointer, and converts
+* the chain to use these physical addresses.
+*
+******
+* This function needs to return some indication that the chain could not be
+* converted. It also needs to back out any conversion already taken place on
+* this chain of links.
+*
+* Then, of course, sah_Physicalise_Descriptors() will need to recognize that
+* an error occured, and then be able to back out any physicalization of the
+* chain which had taken place up to that point!
+******
+*
+* @brief Convert kernel Link chain
+*
+* @param first_link A sah_Link pointer from kernel space; must not be
+* NULL, so error case can be distinguished.
+*
+* @return sah_Link * A dma'able address of the first descriptor in the
+* chain.
+* @return NULL If Link chain could not be physicalised, i.e. ERROR
+*
+*/
+sah_Link *sah_Physicalise_Links(sah_Link * first_link)
+{
+ sah_Link *link = first_link;
+
+ while (link != NULL) {
+#ifdef DO_DBG
+ sah_Dump_Words("Link", (unsigned *)link, link->dma_addr, 3);
+#endif
+ link->vm_info = NULL;
+
+ /* need to retrieve stored key? */
+ if (link->flags & SAH_STORED_KEY_INFO) {
+ uint32_t max_len = 0; /* max slot length */
+ fsl_shw_return_t ret_status;
+
+ /* get length and physical address of stored key */
+ ret_status = system_keystore_get_slot_info(link->ownerid, link->slot, (uint32_t *) & link->data, /* RED key address */
+ &max_len);
+ if ((ret_status != FSL_RETURN_OK_S) || (link->len > max_len)) {
+ /* trying to illegally/incorrectly access a key. Cause the
+ * error status register to show a Link Length Error by
+ * putting a zero in the links length. */
+ link->len = 0; /* Cause error. Somebody is up to no good. */
+ }
+ } else if (link->flags & SAH_IN_USER_KEYSTORE) {
+
+#ifdef FSL_HAVE_SCC2
+ /* The data field points to the virtual address of the key. Convert
+ * this to a physical address by modifying the address based
+ * on where the secure memory was mapped to the kernel. Note: In
+ * kernel mode, no attempt is made to track or control who owns what
+ * memory partition.
+ */
+ link->data = (uint8_t *) scc_virt_to_phys(link->data);
+
+ /* Do bounds checking to ensure that the user is not overstepping
+ * the bounds of their partition. This is a simple implementation
+ * that assumes the user only owns one partition. It only checks
+ * to see if the address of the last byte of data steps over a
+ * page boundary.
+ */
+
+#ifdef DO_DBG
+ LOG_KDIAG_ARGS("start page: %08x, end page: %08x"
+ "first addr: %p, last addr: %p, len; %i",
+ ((uint32_t) (link->data) >> PAGE_SHIFT),
+ (((uint32_t) link->data +
+ link->len) >> PAGE_SHIFT), link->data,
+ link->data + link->len, link->len);
+#endif
+
+ if ((((uint32_t) link->data +
+ link->len) >> PAGE_SHIFT) !=
+ ((uint32_t) link->data >> PAGE_SHIFT)) {
+ link->len = 0; /* Cause error. Somebody is up to no good. */
+ }
+#else /* FSL_HAVE_SCC2 */
+
+ /* User keystores are not valid on non-SCC2 platforms */
+ link->len = 0; /* Cause error. Somebody is up to no good. */
+
+#endif /* FSL_HAVE_SCC2 */
+
+ } else {
+ if (!(link->flags & SAH_PREPHYS_DATA)) {
+ link->original_data = link->data;
+
+ /* All pointers are virtual right now */
+ link->data = (void *)os_pa(link->data);
+#ifdef DO_DBG
+ os_printk("%sput: %p (%d)\n",
+ (link->
+ flags & SAH_OUTPUT_LINK) ? "out" :
+ "in", link->data, link->len);
+#endif
+
+ if (link->flags & SAH_OUTPUT_LINK) {
+ /* clean and invalidate */
+ os_cache_flush_range(link->
+ original_data,
+ link->len);
+ } else {
+ os_cache_clean_range(link->original_data,
+ link->len);
+ }
+ } /* not prephys */
+ } /* else not key reference */
+
+#if defined(NO_OUTPUT_1K_CROSSING) || defined(NO_1K_CROSSING)
+ if (
+#ifdef NO_OUTPUT_1K_CROSSING
+ /* Insert extra link if 1k boundary on output pointer
+ * crossed not at an 8-word boundary */
+ (link->flags & SAH_OUTPUT_LINK) &&
+ (((uint32_t) link->data % 32) != 0) &&
+#endif
+ ((((uint32_t) link->data & 1023) + link->len) >
+ 1024)) {
+ uint32_t full_length = link->len;
+ sah_Link *new_link = sah_Alloc_Link();
+ link->len = 1024 - ((uint32_t) link->data % 1024);
+ new_link->len = full_length - link->len;
+ new_link->data = link->data + link->len;
+ new_link->original_data =
+ link->original_data + link->len;
+ new_link->flags = link->flags & ~(SAH_OWNS_LINK_DATA);
+ new_link->flags |= SAH_LINK_INSERTED_LINK;
+ new_link->next = link->next;
+
+ link->next = (sah_Link *) new_link->dma_addr;
+ link->original_next = new_link;
+ link = new_link;
+ }
+#endif /* ALLOW_OUTPUT_1K_CROSSING */
+
+ link->original_next = link->next;
+ if (link->next != NULL) {
+ link->next = (sah_Link *) link->next->dma_addr;
+ }
+#ifdef DO_DBG
+ sah_Dump_Words("Link", link, link->dma_addr, 3);
+#endif
+
+ link = link->original_next;
+ }
+
+ return (sah_Link *) first_link->dma_addr;
+} /* sah_Physicalise_Links */
+
+/*!
+ * Run through descriptors and links created by KM-API and set the
+ * dma addresses and 'do not free' flags.
+ *
+ * @param first_desc KERNEL VIRTUAL address of first descriptor in chain.
+ *
+ * Warning! This ONLY works without LLO flags in headers!!!
+ *
+ * @return Virtual address of @a first_desc.
+ * @return NULL if Descriptor Chain could not be physicalised
+ */
+sah_Head_Desc *sah_Physicalise_Descriptors(sah_Head_Desc * first_desc)
+{
+ sah_Desc *desc = &first_desc->desc;
+
+ if (!(first_desc->uco_flags & FSL_UCO_CHAIN_PREPHYSICALIZED)) {
+ while (desc != NULL) {
+ sah_Desc *next_desc;
+
+#ifdef DO_DBG
+
+ sah_Dump_Words("Desc", (unsigned *)desc, desc->dma_addr, 6);
+#endif
+
+ desc->original_ptr1 = desc->ptr1;
+ if (desc->ptr1 != NULL) {
+ if ((desc->ptr1 =
+ sah_Physicalise_Links(desc->ptr1)) ==
+ NULL) {
+ /* Clean up ... */
+ sah_DePhysicalise_Descriptors
+ (first_desc);
+ first_desc = NULL;
+ break;
+ }
+ }
+ desc->original_ptr2 = desc->ptr2;
+ if (desc->ptr2 != NULL) {
+ if ((desc->ptr2 =
+ sah_Physicalise_Links(desc->ptr2)) ==
+ NULL) {
+ /* Clean up ... */
+ sah_DePhysicalise_Descriptors
+ (first_desc);
+ first_desc = NULL;
+ break;
+ }
+ }
+
+ desc->original_next = desc->next;
+ next_desc = desc->next; /* save for bottom of while loop */
+ if (desc->next != NULL) {
+ desc->next = (sah_Desc *) desc->next->dma_addr;
+ }
+
+ desc = next_desc;
+ }
+ }
+ /* not prephysicalized */
+#ifdef DO_DBG
+ os_printk("Physicalise finished\n");
+#endif
+
+ return first_desc;
+} /* sah_Physicalise_Descriptors() */
+
+/*!
+*******************************************************************************
+* This function runs through a sah_Link chain pointed to by a physical address.
+* It computes the virtual address for each pointer
+*
+* @brief Convert physical Link chain
+*
+* @param first_link A kernel address of a sah_Link
+*
+* @return sah_Link * A kernal address for the link chain of @c first_link
+* @return NULL If there was some error.
+*
+* @post All links will be chained together by original virtual addresses,
+* data pointers will point to virtual addresses. Appropriate cache
+* lines will be flushed, memory unwired, etc.
+*/
+sah_Link *sah_DePhysicalise_Links(sah_Link * first_link)
+{
+ sah_Link *link = first_link;
+ sah_Link *prev_link = NULL;
+
+ /* Loop on virtual link pointer */
+ while (link != NULL) {
+
+#ifdef DO_DBG
+ sah_Dump_Words("Link", (unsigned *)link, link->dma_addr, 3);
+#endif
+
+ /* if this references stored keys, don't want to dephysicalize them */
+ if (!(link->flags & SAH_STORED_KEY_INFO)
+ && !(link->flags & SAH_PREPHYS_DATA)
+ && !(link->flags & SAH_IN_USER_KEYSTORE)) {
+
+ /* */
+ if (link->flags & SAH_OUTPUT_LINK) {
+ os_cache_inv_range(link->original_data,
+ link->len);
+ }
+
+ /* determine if there is a page in user space associated with this
+ * link */
+ if (link->vm_info != NULL) {
+ /* check that this isn't reserved and contains output */
+ if (!PageReserved(link->vm_info) &&
+ (link->flags & SAH_OUTPUT_LINK)) {
+
+ /* Mark to force page eventually to backing store */
+ SetPageDirty(link->vm_info);
+ }
+
+ /* Untie this page from physical memory */
+ page_cache_release(link->vm_info);
+ } else {
+ /* kernel-mode data */
+#ifdef DO_DBG
+ os_printk("%sput: %p (%d)\n",
+ (link->
+ flags & SAH_OUTPUT_LINK) ? "out" :
+ "in", link->original_data, link->len);
+#endif
+ }
+ link->data = link->original_data;
+ }
+#ifndef ALLOW_OUTPUT_1K_CROSSING
+ if (link->flags & SAH_LINK_INSERTED_LINK) {
+ /* Reconsolidate data by merging this link with previous */
+ prev_link->len += link->len;
+ prev_link->next = link->next;
+ prev_link->original_next = link->original_next;
+ sah_Free_Link(link);
+ link = prev_link;
+
+ }
+#endif
+
+ if (link->next != NULL) {
+ link->next = link->original_next;
+ }
+ prev_link = link;
+ link = link->next;
+ }
+
+ return first_link;
+} /* sah_DePhysicalise_Links() */
+
+/*!
+ * Run through descriptors and links that have been Physicalised
+ * (sah_Physicalise_Descriptors function) and set the dma addresses back
+ * to KM virtual addresses
+ *
+ * @param first_desc Kernel virtual address of first descriptor in chain.
+ *
+ * Warning! This ONLY works without LLO flags in headers!!!
+ */
+sah_Head_Desc *sah_DePhysicalise_Descriptors(sah_Head_Desc * first_desc)
+{
+ sah_Desc *desc = &first_desc->desc;
+
+ if (!(first_desc->uco_flags & FSL_UCO_CHAIN_PREPHYSICALIZED)) {
+ while (desc != NULL) {
+#ifdef DO_DBG
+ sah_Dump_Words("Desc", (unsigned *)desc, desc->dma_addr, 6);
+#endif
+
+ if (desc->ptr1 != NULL) {
+ desc->ptr1 =
+ sah_DePhysicalise_Links(desc->
+ original_ptr1);
+ }
+ if (desc->ptr2 != NULL) {
+ desc->ptr2 =
+ sah_DePhysicalise_Links(desc->
+ original_ptr2);
+ }
+ if (desc->next != NULL) {
+ desc->next = desc->original_next;
+ }
+ desc = desc->next;
+ }
+ }
+ /* not prephysicalized */
+ return first_desc;
+} /* sah_DePhysicalise_Descriptors() */
+
+/*!
+*******************************************************************************
+* This walks through a SAHARA descriptor chain and free()'s everything
+* that is not NULL. Finally it also unmaps all of the physical memory and
+* frees the kiobuf_list Queue.
+*
+* @brief Kernel Descriptor Chain Destructor
+*
+* @param head_desc A Descriptor pointer from kernel-space.
+*
+* @return void
+*
+*/
+void sah_Free_Chained_Descriptors(sah_Head_Desc * head_desc)
+{
+ sah_Desc *desc = NULL;
+ sah_Desc *next_desc = NULL;
+ int this_is_head = 1;
+
+ desc = &head_desc->desc;
+
+ while (desc != NULL) {
+
+ sah_Free_Chained_Links(desc->ptr1);
+ sah_Free_Chained_Links(desc->ptr2);
+
+ /* Get a bus pointer to the next Descriptor */
+ next_desc = desc->next;
+
+ /* Zero the header and Length fields for security reasons. */
+ desc->header = 0;
+ desc->len1 = 0;
+ desc->len2 = 0;
+
+ if (this_is_head) {
+ sah_Free_Head_Descriptor(head_desc);
+ this_is_head = 0;
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "Free_Head_Descriptor: %p\n",
+ head_desc);
+ LOG_KDIAG(Diag_msg);
+#endif
+ } else {
+ /* free this descriptor */
+ sah_Free_Descriptor(desc);
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "Free_Descriptor: %p\n", desc);
+ LOG_KDIAG(Diag_msg);
+#endif
+ }
+
+ /* Look at the next Descriptor */
+ desc = next_desc;
+ }
+} /* sah_Free_Chained_Descriptors() */
+
+/*!
+*******************************************************************************
+* This walks through a SAHARA link chain and frees everything that is
+* not NULL, excluding user-space buffers.
+*
+* @brief Kernel Link Chain Destructor
+*
+* @param link A Link pointer from kernel-space. This is in bus address
+* space.
+*
+* @return void
+*
+*/
+void sah_Free_Chained_Links(sah_Link * link)
+{
+ sah_Link *next_link = NULL;
+
+ while (link != NULL) {
+ /* Get a bus pointer to the next Link */
+ next_link = link->next;
+
+ /* Zero some fields for security reasons. */
+ link->data = NULL;
+ link->len = 0;
+ link->ownerid = 0;
+
+ /* Free this Link */
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "Free_Link: %p(->%p)\n", link, link->next);
+ LOG_KDIAG(Diag_msg);
+#endif
+ sah_Free_Link(link);
+
+ /* Move on to the next Link */
+ link = next_link;
+ }
+}
+
+/*!
+*******************************************************************************
+* This function runs through a link chain pointed to by a user-space
+* address. It makes a temporary kernel-space copy of each link in the
+* chain and calls sah_Make_Links() to create a set of kernel-side links
+* to replace it.
+*
+* @brief Kernel Link Chain Copier
+*
+* @param ptr A link pointer from user-space.
+*
+* @return sah_Link * - The virtual address of the first link in the
+* chain.
+* @return NULL - If there was some error.
+*/
+sah_Link *sah_Copy_Links(fsl_shw_uco_t * user_ctx, sah_Link * ptr)
+{
+ sah_Link *head_link = NULL;
+ sah_Link *new_head_link = NULL;
+ sah_Link *new_tail_link = NULL;
+ sah_Link *prev_tail_link = NULL;
+ sah_Link *user_link = ptr;
+ sah_Link link_copy;
+ int link_data_length = 0;
+
+ /* Internal status variable to be used in this function */
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Link *ret_val = NULL;
+
+ /* This will be set to True when we have finished processing our
+ * link chain. */
+ int drv_if_done = FALSE;
+ int is_this_the_head = TRUE;
+ int result;
+
+ /* transfer all links, on this link chain, from user space */
+ while (drv_if_done == FALSE) {
+ /* Copy the current link from user-space. The virtual address, DMA
+ * address, and vm_info fields of the sah_Link struct are not part
+ * of the user-space structure. They must be the last elements and
+ * should not be copied. */
+ result = copy_from_user(&link_copy,
+ user_link, (sizeof(sah_Link) -
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ sizeof(struct page *) - /* vm_info */
+#endif
+ sizeof(dma_addr_t) - /* dma_addr */
+ sizeof(uint32_t) - /* virt_addr */
+ sizeof(uint8_t *) - /* original_data */
+ sizeof(sah_Link *))); /* original_next */
+
+ if (result != 0) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("copy_from_user() failed.");
+#endif
+ drv_if_done = TRUE;
+ status = FSL_RETURN_INTERNAL_ERROR_S;
+ }
+
+ if (status == FSL_RETURN_OK_S) {
+ /* This will create new links which can be used to replace tmp_link
+ * in the chain. This will return a new head and tail link. */
+ link_data_length = link_data_length + link_copy.len;
+ new_head_link =
+ sah_Make_Links(user_ctx, &link_copy, &new_tail_link);
+
+ if (new_head_link == NULL) {
+ /* If we ran out of memory or a user pointer was invalid */
+ drv_if_done = TRUE;
+ status = FSL_RETURN_INTERNAL_ERROR_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("sah_Make_Links() failed.");
+#endif
+ } else {
+ if (is_this_the_head == TRUE) {
+ /* Keep a reference to the head link */
+ head_link = new_head_link;
+ is_this_the_head = FALSE;
+ } else {
+ /* Need to update the previous links' next field to point
+ * to the current link. */
+ prev_tail_link->next =
+ (void *)new_head_link->dma_addr;
+ prev_tail_link->original_next =
+ new_head_link;
+ }
+ }
+ }
+
+ if (status == FSL_RETURN_OK_S) {
+ /* Get to the next link in the chain. */
+ user_link = link_copy.next;
+ prev_tail_link = new_tail_link;
+
+ /* Check if the end of the link chain was reached (TRUE) or if
+ * there is another linked to this one (FALSE) */
+ drv_if_done = (user_link == NULL) ? TRUE : FALSE;
+ }
+ } /* end while */
+
+ if (status != FSL_RETURN_OK_S) {
+ ret_val = NULL;
+ /* There could be clean-up to do here because we may have made some
+ * successful iterations through the while loop and as a result, the
+ * links created by sah_Make_Links() need to be destroyed.
+ */
+ if (head_link != NULL) {
+ /* Failed somewhere in the while loop and need to clean-up. */
+ sah_Destroy_Links(head_link);
+ }
+ } else {
+ /* Success. Return the head link. */
+ ret_val = head_link;
+ }
+
+ return ret_val;
+} /* sah_Copy_Links() */
+
+/*!
+*******************************************************************************
+* This function takes an input link pointed to by a user-space address
+* and returns a chain of links that span the physical pages pointed
+* to by the input link.
+*
+* @brief Kernel Link Chain Constructor
+*
+* @param ptr A link pointer from user-space.
+* @param tail The address of a link pointer. This is used to return
+* the tail link created by this function.
+*
+* @return sah_Link * - A virtual address of the first link in the
+* chain.
+* @return NULL - If there was some error.
+*
+*/
+sah_Link *sah_Make_Links(fsl_shw_uco_t * user_ctx,
+ sah_Link * ptr, sah_Link ** tail)
+{
+ int result = -1;
+ int page_index = 0;
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ int is_this_the_head = TRUE;
+ void *buffer_start = NULL;
+ sah_Link *link = NULL;
+ sah_Link *prev_link = NULL;
+ sah_Link *head_link = NULL;
+ sah_Link *ret_val = NULL;
+ int buffer_length = 0;
+ struct page **local_pages = NULL;
+ int nr_pages = 0;
+ int write = (sah_Link_Get_Flags(ptr) & SAH_OUTPUT_LINK) ? WRITE : READ;
+ dma_addr_t phys_addr;
+
+ /* need to retrieve stored key? */
+ if (ptr->flags & SAH_STORED_KEY_INFO) {
+ fsl_shw_return_t ret_status;
+
+ /* allocate space for this link */
+ link = sah_Alloc_Link();
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "Alloc_Link returned %p/%p\n", link,
+ (void *)link->dma_addr);
+ LOG_KDIAG(Diag_msg);
+#endif
+
+ if (link == NULL) {
+ status = FSL_RETURN_NO_RESOURCE_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("sah_Alloc_Link() failed!");
+#endif
+ return link;
+ } else {
+ uint32_t max_len = 0; /* max slot length */
+
+ /* get length and physical address of stored key */
+ ret_status = system_keystore_get_slot_info(ptr->ownerid, ptr->slot, (uint32_t *) & link->data, /* RED key address */
+ &max_len);
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS
+ ("ret_status==SCC_RET_OK? %s. slot: %i. data: %p"
+ ". len: %i, key length: %i",
+ (ret_status == FSL_RETURN_OK_S ? "yes" : "no"),
+ ptr->slot, link->data, max_len, ptr->len);
+#endif
+
+ if ((ret_status == FSL_RETURN_OK_S) && (ptr->len <= max_len)) {
+ /* finish populating the link */
+ link->len = ptr->len;
+ link->flags = ptr->flags & ~SAH_PREPHYS_DATA;
+ *tail = link;
+ } else {
+#ifdef DIAG_DRV_IF
+ if (ret_status == FSL_RETURN_OK_S) {
+ LOG_KDIAG
+ ("SCC sah_Link key slot reference is too long");
+ } else {
+ LOG_KDIAG
+ ("SCC sah_Link slot slot reference is invalid");
+ }
+#endif
+ sah_Free_Link(link);
+ status = FSL_RETURN_INTERNAL_ERROR_S;
+ return NULL;
+ }
+ return link;
+ }
+ } else if (ptr->flags & SAH_IN_USER_KEYSTORE) {
+
+#ifdef FSL_HAVE_SCC2
+
+ void *kernel_base;
+
+ /* allocate space for this link */
+ link = sah_Alloc_Link();
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "Alloc_Link returned %p/%p\n", link,
+ (void *)link->dma_addr);
+ LOG_KDIAG(Diag_msg);
+#endif /* DIAG_MEM */
+
+ if (link == NULL) {
+ status = FSL_RETURN_NO_RESOURCE_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("sah_Alloc_Link() failed!");
+#endif
+ return link;
+ } else {
+ /* link->data points to the virtual address of the key data, however
+ * this memory does not need to be locked down.
+ */
+ kernel_base = lookup_user_partition(user_ctx,
+ (uint32_t) ptr->
+ data & PAGE_MASK);
+
+ link->data = (uint8_t *) scc_virt_to_phys(kernel_base +
+ ((unsigned
+ long)ptr->
+ data &
+ ~PAGE_MASK));
+
+ /* Do bounds checking to ensure that the user is not overstepping
+ * the bounds of their partition. This is a simple implementation
+ * that assumes the user only owns one partition. It only checks
+ * to see if the address of the last byte of data steps over a
+ * page boundary.
+ */
+ if ((kernel_base != NULL) &&
+ ((((uint32_t) link->data +
+ link->len) >> PAGE_SHIFT) ==
+ ((uint32_t) link->data >> PAGE_SHIFT))) {
+ /* finish populating the link */
+ link->len = ptr->len;
+ link->flags = ptr->flags & ~SAH_PREPHYS_DATA;
+ *tail = link;
+ } else {
+#ifdef DIAG_DRV_IF
+ if (kernel_base != NULL) {
+ LOG_KDIAG
+ ("SCC sah_Link key slot reference is too long");
+ } else {
+ LOG_KDIAG
+ ("SCC sah_Link slot slot reference is invalid");
+ }
+#endif
+ sah_Free_Link(link);
+ status = FSL_RETURN_INTERNAL_ERROR_S;
+ return NULL;
+ }
+ return link;
+ }
+
+#else /* FSL_HAVE_SCC2 */
+
+ return NULL;
+
+#endif /* FSL_HAVE_SCC2 */
+ }
+
+ if (ptr->data == NULL) {
+ /* The user buffer must not be NULL because map_user_kiobuf() cannot
+ * handle NULL pointer input.
+ */
+ status = FSL_RETURN_BAD_DATA_LENGTH_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("sah_Link data pointer is NULL.");
+#endif
+ }
+
+ if (status == FSL_RETURN_OK_S) {
+ unsigned long start_page = (unsigned long)ptr->data & PAGE_MASK;
+
+ /* determine number of pages being used for this link */
+ nr_pages = (((unsigned long)(ptr->data) & ~PAGE_MASK)
+ + ptr->len + ~PAGE_MASK) >> PAGE_SHIFT;
+
+ /* ptr contains all the 'user space' information, add the pages
+ * to it also just so everything is in one place */
+ local_pages =
+ kmalloc(nr_pages * sizeof(struct page *), GFP_KERNEL);
+
+ if (local_pages == NULL) {
+ status = FSL_RETURN_NO_RESOURCE_S; /* no memory! */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("kmalloc() failed.");
+#endif
+ } else {
+ /* get the actual pages being used in 'user space' */
+
+ down_read(&current->mm->mmap_sem);
+ result = get_user_pages(current, current->mm,
+ start_page, nr_pages,
+ write, 0 /* noforce */ ,
+ local_pages, NULL);
+ up_read(&current->mm->mmap_sem);
+
+ if (result < nr_pages) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("get_user_pages() failed.");
+#endif
+ if (result > 0) {
+ for (page_index = 0;
+ page_index < result;
+ page_index++) {
+ page_cache_release(local_pages
+ [page_index]);
+ }
+ }
+ status = FSL_RETURN_INTERNAL_ERROR_S;
+ }
+ }
+ }
+
+ /* Now we can walk through the list of pages in the buffer */
+ if (status == FSL_RETURN_OK_S) {
+
+#if defined(FLUSH_SPECIFIC_DATA_ONLY) && !defined(HAS_L2_CACHE)
+ /*
+ * Now that pages are wired, clear user data from cache lines. When
+ * there is just an L1 cache, clean based on user virtual for ARM.
+ */
+ if (write == WRITE) {
+ os_cache_flush_range(ptr->data, ptr->len);
+ } else {
+ os_cache_clean_range(ptr->data, ptr->len);
+ }
+#endif
+
+ for (page_index = 0; page_index < nr_pages; page_index++) {
+ /* Allocate a new link structure */
+ link = sah_Alloc_Link();
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "Alloc_Link returned %p/%p\n", link,
+ (void *)link->dma_addr);
+ LOG_KDIAG(Diag_msg);
+#endif
+ if (link == NULL) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("sah_Alloc_Link() failed.");
+#endif
+ status = FSL_RETURN_NO_RESOURCE_S;
+
+ /* need to free the rest of the pages. Destroy_Links will take
+ * care of the ones already assigned to a link */
+ for (; page_index < nr_pages; page_index++) {
+ page_cache_release(local_pages
+ [page_index]);
+ }
+ break; /* exit 'for page_index' loop */
+ }
+
+ if (status == FSL_RETURN_OK_S) {
+ if (is_this_the_head == TRUE) {
+ /* keep a reference to the head link */
+ head_link = link;
+ /* remember that we have seen the head link */
+ is_this_the_head = FALSE;
+ } else {
+ /* If this is not the head link then set the previous
+ * link's next pointer to point to this link */
+ prev_link->original_next = link;
+ prev_link->next =
+ (sah_Link *) link->dma_addr;
+ }
+
+ buffer_start =
+ page_address(local_pages[page_index]);
+
+ phys_addr =
+ page_to_phys(local_pages[page_index]);
+
+ if (page_index == 0) {
+ /* If this is the first page, there might be an
+ * offset. We need to increment the address by this offset
+ * so we don't just get the start of the page.
+ */
+ buffer_start +=
+ (unsigned long)
+ sah_Link_Get_Data(ptr)
+ & ~PAGE_MASK;
+ phys_addr +=
+ (unsigned long)
+ sah_Link_Get_Data(ptr)
+ & ~PAGE_MASK;
+ buffer_length = PAGE_SIZE
+ -
+ ((unsigned long)
+ sah_Link_Get_Data(ptr)
+ & ~PAGE_MASK);
+ } else {
+ buffer_length = PAGE_SIZE;
+ }
+
+ if (page_index == nr_pages - 1) {
+ /* if this is the last page, we need to adjust
+ * the buffer_length to account for the last page being
+ * partially used.
+ */
+ buffer_length -=
+ nr_pages * PAGE_SIZE -
+ sah_Link_Get_Len(ptr) -
+ ((unsigned long)
+ sah_Link_Get_Data(ptr) &
+ ~PAGE_MASK);
+ }
+#if defined(FLUSH_SPECIFIC_DATA_ONLY) && defined(HAS_L2_CACHE)
+ /*
+ * When there is an L2 cache, clean based on kernel
+ * virtual..
+ */
+ if (write == WRITE) {
+ os_cache_flush_range(buffer_start,
+ buffer_length);
+ } else {
+ os_cache_clean_range(buffer_start,
+ buffer_length);
+ }
+#endif
+
+ /* Fill in link information */
+ link->len = buffer_length;
+#if !defined(HAS_L2_CACHE)
+ /* use original virtual */
+ link->original_data = ptr->data;
+#else
+ /* use kernel virtual */
+ link->original_data = buffer_start;
+#endif
+ link->data = (void *)phys_addr;
+ link->flags = ptr->flags & ~SAH_PREPHYS_DATA;
+ link->vm_info = local_pages[page_index];
+ prev_link = link;
+
+#if defined(NO_OUTPUT_1K_CROSSING) || defined(NO_1K_CROSSING)
+ if (
+#ifdef NO_OUTPUT_1K_CROSSING
+ /* Insert extra link if 1k boundary on output pointer
+ * crossed not at an 8-word boundary */
+ (link->flags & SAH_OUTPUT_LINK) &&
+ (((uint32_t) buffer_start % 32) != 0)
+ &&
+#endif
+ ((((uint32_t) buffer_start & 1023) +
+ buffer_length) > 1024)) {
+
+ /* Shorten current link to 1k boundary */
+ link->len =
+ 1024 -
+ ((uint32_t) buffer_start % 1024);
+
+ /* Get new link to follow it */
+ link = sah_Alloc_Link();
+ prev_link->len =
+ 1024 -
+ ((uint32_t) buffer_start % 1024);
+ prev_link->original_next = link;
+ prev_link->next =
+ (sah_Link *) link->dma_addr;
+ buffer_length -= prev_link->len;
+ buffer_start += prev_link->len;
+
+#if !defined(HAS_L2_CACHE)
+ /* use original virtual */
+ link->original_data = ptr->data;
+#else
+ /* use kernel virtual */
+ link->original_data = buffer_start;
+#endif
+ link->data = (void *)phys_addr;
+ link->vm_info = prev_link->vm_info;
+ prev_link->vm_info = NULL; /* delay release */
+ link->flags = ptr->flags;
+ link->len = buffer_length;
+ prev_link = link;
+ } /* while link would cross 1K boundary */
+#endif /* 1K_CROSSING */
+ }
+ } /* for each page */
+ }
+
+ if (local_pages != NULL) {
+ kfree(local_pages);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ /* De-allocated any links created, this routine first looks if
+ * head_link is NULL */
+ sah_Destroy_Links(head_link);
+
+ /* Clean-up of the KIOBUF will occur in the * sah_Copy_Descriptors()
+ * function.
+ * Clean-up of the Queue entry must occur in the function called
+ * sah_Copy_Descriptors().
+ */
+ } else {
+
+ /* Success. Return the head link. */
+ ret_val = head_link;
+ link->original_next = NULL;
+ /* return the tail link as well */
+ *tail = link;
+ }
+
+ return ret_val;
+} /* sah_Make_Links() */
+
+/*!
+*******************************************************************************
+* This walks through a SAHARA descriptor chain and frees everything
+* that is not NULL. Finally it also unmaps all of the physical memory and
+* frees the kiobuf_list Queue.
+*
+* @brief Kernel Descriptor Chain Destructor
+*
+* @param desc A Descriptor pointer from kernel-space. This should be
+* in bus address space.
+*
+* @return void
+*
+*/
+void sah_Destroy_Descriptors(sah_Head_Desc * head_desc)
+{
+ sah_Desc *this_desc = (sah_Desc *) head_desc;
+ sah_Desc *next_desc = NULL;
+ int this_is_head = 1;
+
+ /*
+ * Flush the D-cache. This flush is here because the hardware has finished
+ * processing this descriptor and probably has changed the contents of
+ * some linked user buffers as a result. This flush will enable
+ * user-space applications to see the correct data rather than the
+ * out-of-date cached version.
+ */
+#ifndef FLUSH_SPECIFIC_DATA_ONLY
+ os_flush_cache_all();
+#endif
+
+ head_desc = (sah_Head_Desc *) this_desc->virt_addr;
+
+ while (this_desc != NULL) {
+ if (this_desc->ptr1 != NULL) {
+ sah_Destroy_Links(this_desc->original_ptr1
+ ? this_desc->
+ original_ptr1 : this_desc->ptr1);
+ }
+ if (this_desc->ptr2 != NULL) {
+ sah_Destroy_Links(this_desc->original_ptr2
+ ? this_desc->
+ original_ptr2 : this_desc->ptr2);
+ }
+
+ /* Get a bus pointer to the next Descriptor */
+ next_desc = (this_desc->original_next
+ ? this_desc->original_next : this_desc->next);
+
+ /* Zero the header and Length fields for security reasons. */
+ this_desc->header = 0;
+ this_desc->len1 = 0;
+ this_desc->len2 = 0;
+
+ if (this_is_head) {
+ sah_Free_Head_Descriptor(head_desc);
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "Free_Head_Descriptor: %p\n",
+ head_desc);
+ LOG_KDIAG(Diag_msg);
+#endif
+ this_is_head = 0;
+ } else {
+ /* free this descriptor */
+ sah_Free_Descriptor(this_desc);
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "Free_Descriptor: %p\n", this_desc);
+ LOG_KDIAG(Diag_msg);
+#endif
+ }
+
+ /* Set up for next round. */
+ this_desc = (sah_Desc *) next_desc;
+ }
+}
+
+/*!
+*******************************************************************************
+* This walks through a SAHARA link chain and frees everything that is
+* not NULL excluding user-space buffers.
+*
+* @brief Kernel Link Chain Destructor
+*
+* @param link A Link pointer from kernel-space.
+*
+* @return void
+*
+*/
+void sah_Destroy_Links(sah_Link * link)
+{
+ sah_Link *this_link = link;
+ sah_Link *next_link = NULL;
+
+ while (this_link != NULL) {
+
+ /* if this link indicates an associated page, process it */
+ if (this_link->vm_info != NULL) {
+ /* since this function is only called from the routine that
+ * creates a kernel copy of the user space descriptor chain,
+ * there are no pages to dirty. All that is needed is to release
+ * the page from cache */
+ page_cache_release(this_link->vm_info);
+ }
+
+ /* Get a bus pointer to the next Link */
+ next_link = (this_link->original_next
+ ? this_link->original_next : this_link->next);
+
+ /* Zero the Pointer and Length fields for security reasons. */
+ this_link->data = NULL;
+ this_link->len = 0;
+
+ /* Free this Link */
+ sah_Free_Link(this_link);
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "Free_Link: %p\n", this_link);
+ LOG_KDIAG(Diag_msg);
+#endif
+
+ /* Look at the next Link */
+ this_link = next_link;
+ }
+}
+
+/*!
+*******************************************************************************
+* @brief Initialize memory manager/mapper.
+*
+* In 2.4, this function also allocates a kiovec to be used when mapping user
+* data to kernel space
+*
+* @return 0 for success, OS error code on failure
+*
+*/
+int sah_Init_Mem_Map(void)
+{
+ int ret = OS_ERROR_FAIL_S;
+
+ mem_lock = os_lock_alloc_init();
+
+ /*
+ * If one of these fails, change the calculation in the #define earlier in
+ * the file to be the other one.
+ */
+ if (sizeof(sah_Link) > MEM_BLOCK_SIZE) {
+ os_printk("Sahara Driver: sah_Link structure is too large\n");
+ } else if (sizeof(sah_Desc) > MEM_BLOCK_SIZE) {
+ os_printk("Sahara Driver: sah_Desc structure is too large\n");
+ } else {
+ ret = OS_ERROR_OK_S;
+ }
+
+#ifndef SELF_MANAGED_POOL
+
+ big_dma_pool = dma_pool_create("sah_big_blocks", NULL,
+ sizeof(Mem_Big_Block), sizeof(uint32_t),
+ PAGE_SIZE);
+ small_dma_pool = dma_pool_create("sah_small_blocks", NULL,
+ sizeof(Mem_Block), sizeof(uint32_t),
+ PAGE_SIZE);
+#else
+
+#endif
+ return ret;
+}
+
+/*!
+*******************************************************************************
+* @brief Clean up memory manager/mapper.
+*
+* In 2.4, this function also frees the kiovec used when mapping user data to
+* kernel space.
+*
+* @return none
+*
+*/
+void sah_Stop_Mem_Map(void)
+{
+ os_lock_deallocate(mem_lock);
+
+#ifndef SELF_MANAGED_POOL
+ if (big_dma_pool != NULL) {
+ dma_pool_destroy(big_dma_pool);
+ }
+ if (small_dma_pool != NULL) {
+ dma_pool_destroy(small_dma_pool);
+ }
+#endif
+}
+
+/*!
+*******************************************************************************
+* Allocate Head descriptor from free pool.
+*
+* @brief Allocate Head descriptor
+*
+* @return sah_Head_Desc Free descriptor, NULL if no free descriptors available.
+*
+*/
+sah_Head_Desc *sah_Alloc_Head_Descriptor(void)
+{
+ Mem_Big_Block *block;
+ sah_Head_Desc *desc;
+
+ block = sah_Alloc_Big_Block();
+ if (block != NULL) {
+ /* initialize everything */
+ desc = (sah_Head_Desc *) block->data;
+
+ desc->desc.virt_addr = (sah_Desc *) desc;
+ desc->desc.dma_addr = block->dma_addr;
+ desc->desc.original_ptr1 = NULL;
+ desc->desc.original_ptr2 = NULL;
+ desc->desc.original_next = NULL;
+
+ desc->desc.ptr1 = NULL;
+ desc->desc.ptr2 = NULL;
+ desc->desc.next = NULL;
+ } else {
+ desc = NULL;
+ }
+
+ return desc;
+}
+
+/*!
+*******************************************************************************
+* Allocate descriptor from free pool.
+*
+* @brief Allocate descriptor
+*
+* @return sah_Desc Free descriptor, NULL if no free descriptors available.
+*
+*/
+sah_Desc *sah_Alloc_Descriptor(void)
+{
+ Mem_Block *block;
+ sah_Desc *desc;
+
+ block = sah_Alloc_Block();
+ if (block != NULL) {
+ /* initialize everything */
+ desc = (sah_Desc *) block->data;
+
+ desc->virt_addr = desc;
+ desc->dma_addr = block->dma_addr;
+ desc->original_ptr1 = NULL;
+ desc->original_ptr2 = NULL;
+ desc->original_next = NULL;
+
+ desc->ptr1 = NULL;
+ desc->ptr2 = NULL;
+ desc->next = NULL;
+ } else {
+ desc = NULL;
+ }
+
+ return (desc);
+}
+
+/*!
+*******************************************************************************
+* Allocate link from free pool.
+*
+* @brief Allocate link
+*
+* @return sah_Link Free link, NULL if no free links available.
+*
+*/
+sah_Link *sah_Alloc_Link(void)
+{
+ Mem_Block *block;
+ sah_Link *link;
+
+ block = sah_Alloc_Block();
+ if (block != NULL) {
+ /* initialize everything */
+ link = (sah_Link *) block->data;
+
+ link->virt_addr = link;
+ link->original_next = NULL;
+ link->original_data = NULL;
+ /* information found in allocated block */
+ link->dma_addr = block->dma_addr;
+
+ /* Sahara link fields */
+ link->len = 0;
+ link->data = NULL;
+ link->next = NULL;
+
+ /* driver required fields */
+ link->flags = 0;
+ link->vm_info = NULL;
+ } else {
+ link = NULL;
+ }
+
+ return link;
+}
+
+#ifdef SELF_MANAGED_POOL
+/*!
+*******************************************************************************
+* Add a new page to end of block free pool. This will allocate one page and
+* fill the pool with entries, appending to the end.
+*
+* @brief Add page of blocks to block free pool.
+*
+* @pre This function must be called with the #mem_lock held.
+*
+* @param big 0 - make blocks big enough for sah_Desc
+* non-zero - make blocks big enough for sah_Head_Desc
+*
+* @return int TRUE if blocks added succeesfully, FALSE otherwise
+*
+*/
+int sah_Block_Add_Page(int big)
+{
+ void *page;
+ int success;
+ dma_addr_t dma_addr;
+ unsigned block_index;
+ uint32_t dma_offset;
+ unsigned block_entries =
+ big ? MEM_BIG_BLOCK_ENTRIES : MEM_BLOCK_ENTRIES;
+ unsigned block_size = big ? sizeof(Mem_Big_Block) : sizeof(Mem_Block);
+ void *block;
+
+ /* Allocate page of memory */
+#ifndef USE_COHERENT_MEMORY
+ page = os_alloc_memory(PAGE_SIZE, GFP_ATOMIC | __GFP_DMA);
+ dma_addr = os_pa(page);
+#else
+ page = os_alloc_coherent(PAGE_SIZE, &dma_addr, GFP_ATOMIC);
+#endif
+ if (page != NULL) {
+ /*
+ * Find the difference between the virtual address and the DMA
+ * address of the page. This is used later to determine the DMA
+ * address of each individual block.
+ */
+ dma_offset = page - (void *)dma_addr;
+
+ /* Split page into blocks and add to free pool */
+ block = page;
+ for (block_index = 0; block_index < block_entries;
+ block_index++) {
+ if (big) {
+ register Mem_Big_Block *blockp = block;
+ blockp->dma_addr =
+ (uint32_t) (block - dma_offset);
+ sah_Append_Big_Block(blockp);
+ } else {
+ register Mem_Block *blockp = block;
+ blockp->dma_addr =
+ (uint32_t) (block - dma_offset);
+ /* sah_Append_Block must be protected with spin locks. This is
+ * done in sah_Alloc_Block(), which calls
+ * sah_Block_Add_Page() */
+ sah_Append_Block(blockp);
+ }
+ block += block_size;
+ }
+ success = TRUE;
+#ifdef DIAG_MEM
+ LOG_KDIAG("Succeeded in allocating new page");
+#endif
+ } else {
+ success = FALSE;
+#ifdef DIAG_MEM
+ LOG_KDIAG("Failed in allocating new page");
+#endif
+ }
+
+ return success;
+}
+#endif /* SELF_MANAGED_POOL */
+
+#ifdef SELF_MANAGED_POOL
+/*!
+*******************************************************************************
+* Allocate block from free pool. A block is large enough to fit either a link
+* or descriptor.
+*
+* @brief Allocate memory block
+*
+* @return Mem_Block Free block, NULL if no free blocks available.
+*
+*/
+static Mem_Big_Block *sah_Alloc_Big_Block(void)
+{
+ Mem_Big_Block *block;
+ os_lock_context_t lock_flags;
+
+ os_lock_save_context(mem_lock, lock_flags);
+
+ /* If the pool is empty, try to allocate more entries */
+ if (big_block_free_head == NULL) {
+ (void)sah_Block_Add_Page(1);
+ }
+
+ /* Check that the pool now has some free entries */
+ if (big_block_free_head != NULL) {
+ /* Return the head of the free pool */
+ block = big_block_free_head;
+
+ big_block_free_head = big_block_free_head->next;
+ if (big_block_free_head == NULL) {
+ /* Allocated last entry in pool */
+ big_block_free_tail = NULL;
+ }
+ } else {
+ block = NULL;
+ }
+ os_unlock_restore_context(mem_lock, lock_flags);
+
+ return block;
+}
+#else
+/*!
+*******************************************************************************
+* Allocate block from free pool. A block is large enough to fit either a link
+* or descriptor.
+*
+* @brief Allocate memory block
+*
+* @return Mem_Block Free block, NULL if no free blocks available.
+*
+*/
+static Mem_Big_Block *sah_Alloc_Big_Block(void)
+{
+ dma_addr_t dma_addr;
+ Mem_Big_Block *block =
+ dma_pool_alloc(big_dma_pool, GFP_ATOMIC, &dma_addr);
+
+ if (block == NULL) {
+ } else {
+ block->dma_addr = dma_addr;
+ }
+
+ return block;
+}
+#endif
+
+#ifdef SELF_MANAGED_POOL
+/*!
+*******************************************************************************
+* Allocate block from free pool. A block is large enough to fit either a link
+* or descriptor.
+*
+* @brief Allocate memory block
+*
+* @return Mem_Block Free block, NULL if no free blocks available.
+*
+*/
+/******************************************************************************
+*
+* MODIFICATION HISTORY:
+*
+* Date Person Change
+* 31/10/2003 RWK PR52734 - Implement functions to allocate
+* descriptors and links. Replace
+* consistent_alloc() calls. Initial creation.
+*
+******************************************************************************/
+static Mem_Block *sah_Alloc_Block(void)
+{
+ Mem_Block *block;
+ os_lock_context_t lock_flags;
+
+ os_lock_save_context(mem_lock, lock_flags);
+
+ /* If the pool is empty, try to allocate more entries */
+ if (block_free_head == NULL) {
+ (void)sah_Block_Add_Page(0);
+ }
+
+ /* Check that the pool now has some free entries */
+ if (block_free_head != NULL) {
+ /* Return the head of the free pool */
+ block = block_free_head;
+
+ block_free_head = block_free_head->next;
+ if (block_free_head == NULL) {
+ /* Allocated last entry in pool */
+ block_free_tail = NULL;
+ }
+ } else {
+ block = NULL;
+ }
+ os_unlock_restore_context(mem_lock, lock_flags);
+
+ return block;
+}
+#else
+/*!
+*******************************************************************************
+* Allocate block from free pool. A block is large enough to fit either a link
+* or descriptor.
+*
+* @brief Allocate memory block
+*
+* @return Mem_Block Free block, NULL if no free blocks available.
+*
+*/
+/******************************************************************************
+*
+* MODIFICATION HISTORY:
+*
+* Date Person Change
+* 31/10/2003 RWK PR52734 - Implement functions to allocate
+* descriptors and links. Replace
+* consistent_alloc() calls. Initial creation.
+*
+******************************************************************************/
+static Mem_Block *sah_Alloc_Block(void)
+{
+
+ dma_addr_t dma_addr;
+ Mem_Block *block =
+ dma_pool_alloc(small_dma_pool, GFP_ATOMIC, &dma_addr);
+
+ if (block == NULL) {
+ } else {
+ block->dma_addr = dma_addr;
+ }
+
+ return block;
+}
+#endif
+
+#ifdef SELF_MANAGED_POOL
+/*!
+*******************************************************************************
+* Free memory block back to free pool
+*
+* @brief Free memory block
+*
+* @param block A block allocated with sah_Alloc_Block().
+*
+* @return none
+*
+*/
+static void sah_Free_Block(Mem_Block * block)
+{
+ os_lock_context_t lock_flags;
+
+ os_lock_save_context(mem_lock, lock_flags);
+ sah_Append_Block(block);
+ os_unlock_restore_context(mem_lock, lock_flags);
+}
+#else
+/*!
+*******************************************************************************
+* Free memory block back to free pool
+*
+* @brief Free memory block
+*
+* @param block A block allocated with sah_Alloc_Block().
+*
+* @return none
+*
+*/
+static void sah_Free_Block(Mem_Block * block)
+{
+ dma_pool_free(small_dma_pool, block, block->dma_addr);
+}
+#endif
+
+#ifdef SELF_MANAGED_POOL
+/*!
+*******************************************************************************
+* Free memory block back to free pool
+*
+* @brief Free memory block
+*
+* @param block A block allocated with sah_Alloc_Block().
+*
+* @return none
+*
+*/
+static void sah_Free_Big_Block(Mem_Big_Block * block)
+{
+ os_lock_context_t lock_flags;
+
+ os_lock_save_context(mem_lock, lock_flags);
+ sah_Append_Big_Block(block);
+ os_unlock_restore_context(mem_lock, lock_flags);
+}
+#else
+/*!
+*******************************************************************************
+* Free memory block back to free pool
+*
+* @brief Free memory block
+*
+* @param block A block allocated with sah_Alloc_Block().
+*
+* @return none
+*
+*/
+static void sah_Free_Big_Block(Mem_Big_Block * block)
+{
+ dma_pool_free(big_dma_pool, block, block->dma_addr);
+}
+#endif
+
+#ifdef SELF_MANAGED_POOL
+/*!
+*******************************************************************************
+* Append memory block to end of free pool.
+*
+* @param block A block entry
+*
+* @return none
+*
+* @pre This function must be called with the #mem_lock held.
+*
+* @brief Append memory block to free pool
+*/
+static void sah_Append_Big_Block(Mem_Big_Block * block)
+{
+
+ /* Initialise block */
+ block->next = NULL;
+
+ /* Remember that block may be the first in the pool */
+ if (big_block_free_tail != NULL) {
+ big_block_free_tail->next = block;
+ } else {
+ /* Pool is empty */
+ big_block_free_head = block;
+ }
+
+ big_block_free_tail = block;
+}
+
+/*!
+*******************************************************************************
+* Append memory block to end of free pool.
+*
+* @brief Append memory block to free pool
+*
+* @param block A block entry
+*
+* @return none
+*
+* @pre #mem_lock must be held
+*
+*/
+static void sah_Append_Block(Mem_Block * block)
+{
+
+ /* Initialise block */
+ block->next = NULL;
+
+ /* Remember that block may be the first in the pool */
+ if (block_free_tail != NULL) {
+ block_free_tail->next = block;
+ } else {
+ /* Pool is empty */
+ block_free_head = block;
+ }
+
+ block_free_tail = block;
+}
+#endif /* SELF_MANAGED_POOL */
+
+/* End of sah_memory_mapper.c */
diff --git a/drivers/mxc/security/sahara2/sah_queue.c b/drivers/mxc/security/sahara2/sah_queue.c
new file mode 100644
index 000000000000..317a60b92b6b
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_queue.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+* @file sah_queue.c
+*
+* @brief This file provides a FIFO Queue implementation.
+*
+*/
+/******************************************************************************
+*
+* CAUTION:
+*******************************************************************
+*/
+
+/* SAHARA Includes */
+#include <sah_queue_manager.h>
+#ifdef DIAG_DRV_QUEUE
+#include <diagnostic.h>
+#endif
+
+/******************************************************************************
+* Queue Functions
+******************************************************************************/
+
+/*!
+*******************************************************************************
+* This function constructs a new sah_Queue.
+*
+* @brief sah_Queue Constructor
+*
+* @return A pointer to a newly allocated sah_Queue.
+* @return NULL if allocation of memory failed.
+*/
+/******************************************************************************
+*
+* CAUTION: This function may sleep in low-memory situations, as it uses
+* kmalloc ( ..., GFP_KERNEL).
+******************************************************************************/
+sah_Queue *sah_Queue_Construct(void)
+{
+ sah_Queue *q = (sah_Queue *) os_alloc_memory(sizeof(sah_Queue),
+ GFP_KERNEL);
+
+ if (q != NULL) {
+ /* Initialise the queue to an empty state. */
+ q->head = NULL;
+ q->tail = NULL;
+ q->count = 0;
+ }
+#ifdef DIAG_DRV_QUEUE
+ else {
+ LOG_KDIAG("kmalloc() failed.");
+ }
+#endif
+
+ return q;
+}
+
+/*!
+*******************************************************************************
+* This function destroys a sah_Queue.
+*
+* @brief sah_Queue Destructor
+*
+* @param q A pointer to a sah_Queue.
+*
+* @return void
+*/
+/******************************************************************************
+*
+* CAUTION: This function does not free any queue entries.
+*
+******************************************************************************/
+void sah_Queue_Destroy(sah_Queue * q)
+{
+#ifdef DIAG_DRV_QUEUE
+ if (q == NULL) {
+ LOG_KDIAG("Trying to kfree() a NULL pointer.");
+ } else {
+ if (q->count != 0) {
+ LOG_KDIAG
+ ("Trying to destroy a queue that is not empty.");
+ }
+ }
+#endif
+
+ if (q != NULL) {
+ os_free_memory(q);
+ q = NULL;
+ }
+}
+
+/*!
+*******************************************************************************
+* This function appends a sah_Head_Desc to the tail of a sah_Queue.
+*
+* @brief Appends a sah_Head_Desc to a sah_Queue.
+*
+* @param q A pointer to a sah_Queue to append to.
+* @param entry A pointer to a sah_Head_Desc to append.
+*
+* @pre The #desc_queue_lock must be held before calling this function.
+*
+* @return void
+*/
+/******************************************************************************
+*
+* CAUTION: NONE
+******************************************************************************/
+void sah_Queue_Append_Entry(sah_Queue * q, sah_Head_Desc * entry)
+{
+ sah_Head_Desc *tail_entry = NULL;
+
+ if ((q == NULL) || (entry == NULL)) {
+#ifdef DIAG_DRV_QUEUE
+ LOG_KDIAG("Null pointer input.");
+#endif
+ return;
+ }
+
+ if (q->count == 0) {
+ /* The queue is empty */
+ q->head = entry;
+ q->tail = entry;
+ entry->next = NULL;
+ entry->prev = NULL;
+ } else {
+ /* The queue is not empty */
+ tail_entry = q->tail;
+ tail_entry->next = entry;
+ entry->next = NULL;
+ entry->prev = tail_entry;
+ q->tail = entry;
+ }
+ q->count++;
+}
+
+/*!
+*******************************************************************************
+* This function a removes a sah_Head_Desc from the head of a sah_Queue.
+*
+* @brief Removes a sah_Head_Desc from a the head of a sah_Queue.
+*
+* @param q A pointer to a sah_Queue to remove from.
+*
+* @pre The #desc_queue_lock must be held before calling this function.
+*
+* @return void
+*/
+/******************************************************************************
+*
+* CAUTION: This does not kfree() the entry.
+******************************************************************************/
+void sah_Queue_Remove_Entry(sah_Queue * q)
+{
+ sah_Queue_Remove_Any_Entry(q, q->head);
+}
+
+/*!
+*******************************************************************************
+* This function a removes a sah_Head_Desc from anywhere in a sah_Queue.
+*
+* @brief Removes a sah_Head_Desc from anywhere in a sah_Queue.
+*
+* @param qq A pointer to a sah_Queue to remove from.
+* @param entry A pointer to a sah_Head_Desc to remove.
+*
+* @pre The #desc_queue_lock must be held before calling this function.
+*
+* @return void
+*/
+/******************************************************************************
+*
+* CAUTION: This does not kfree() the entry. Does not check to see if the entry
+* actually belongs to the queue.
+******************************************************************************/
+void sah_Queue_Remove_Any_Entry(sah_Queue * q, sah_Head_Desc * entry)
+{
+ sah_Head_Desc *prev_entry = NULL;
+ sah_Head_Desc *next_entry = NULL;
+
+ if ((q == NULL) || (entry == NULL)) {
+#if defined DIAG_DRV_QUEUE && defined DIAG_DURING_INTERRUPT
+ LOG_KDIAG("Null pointer input.");
+#endif
+ return;
+ }
+
+ if (q->count == 1) {
+ /* If q is the only entry in the queue. */
+ q->tail = NULL;
+ q->head = NULL;
+ q->count = 0;
+ } else if (q->count > 1) {
+ /* There are 2 or more entries in the queue. */
+
+#if defined DIAG_DRV_QUEUE && defined DIAG_DURING_INTERRUPT
+ if ((entry->next == NULL) && (entry->prev == NULL)) {
+ LOG_KDIAG
+ ("Queue is not empty yet both next and prev pointers"
+ " are NULL");
+ }
+#endif
+
+ if (entry->next == NULL) {
+ /* If this is the end of the queue */
+ prev_entry = entry->prev;
+ prev_entry->next = NULL;
+ q->tail = prev_entry;
+ } else if (entry->prev == NULL) {
+ /* If this is the head of the queue */
+ next_entry = entry->next;
+ next_entry->prev = NULL;
+ q->head = next_entry;
+ } else {
+ /* If this is somewhere in the middle of the queue */
+ prev_entry = entry->prev;
+ next_entry = entry->next;
+ prev_entry->next = next_entry;
+ next_entry->prev = prev_entry;
+ }
+ q->count--;
+ }
+ /*
+ * Otherwise we are removing an entry from an empty queue.
+ * Don't do anything in the product code
+ */
+#if defined DIAG_DRV_QUEUE && defined DIAG_DURING_INTERRUPT
+ else {
+ LOG_KDIAG("Trying to remove an entry from an empty queue.");
+ }
+#endif
+
+ entry->next = NULL;
+ entry->prev = NULL;
+}
+
+/* End of sah_queue.c */
diff --git a/drivers/mxc/security/sahara2/sah_queue_manager.c b/drivers/mxc/security/sahara2/sah_queue_manager.c
new file mode 100644
index 000000000000..78de3543cd23
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_queue_manager.c
@@ -0,0 +1,1076 @@
+/*
+ * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file sah_queue_manager.c
+ *
+ * @brief This file provides a Queue Manager implementation.
+ *
+ * The Queue Manager manages additions and removal from the queue and updates
+ * the status of queue entries. It also calls sah_HW_* functions to interract
+ * with the hardware.
+*/
+
+#include "portable_os.h"
+
+/* SAHARA Includes */
+#include <sah_driver_common.h>
+#include <sah_queue_manager.h>
+#include <sah_status_manager.h>
+#include <sah_hardware_interface.h>
+#if defined(DIAG_DRV_QUEUE) || defined(DIAG_DRV_STATUS)
+#include <diagnostic.h>
+#endif
+#include <sah_memory_mapper.h>
+
+#ifdef DIAG_DRV_STATUS
+
+#define FSL_INVALID_RETURN 13
+#define MAX_RETURN_STRING_LEN 22
+#endif
+
+/* Defines for parsing value from Error Status register */
+#define SAH_STATUS_MASK 0x07
+#define SAH_ERROR_MASK 0x0F
+#define SAH_CHA_ERR_SOURCE_MASK 0x07
+#define SAH_CHA_ERR_STATUS_MASK 0x0FFF
+#define SAH_DMA_ERR_STATUS_MASK 0x0F
+#define SAH_DMA_ERR_SIZE_MASK 0x03
+#define SAH_DMA_ERR_DIR_MASK 0x01
+
+#define SHA_ERROR_STATUS_CONTINUE 0xFFFFFFFF
+#define SHA_CHA_ERROR_STATUS_DONE 0xFFFFFFFF
+
+/* this maps the error status register's error source 4 bit field to the API
+ * return values. A 0xFFFFFFFF indicates additional fields must be checked to
+ * determine an appropriate return value */
+static sah_Execute_Error sah_Execute_Error_Array[] = {
+ FSL_RETURN_ERROR_S, /* SAH_ERR_NONE */
+ FSL_RETURN_BAD_FLAG_S, /* SAH_ERR_HEADER */
+ FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_ERR_DESC_LENGTH */
+ FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_ERR_DESC_POINTER */
+ FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_ERR_LINK_LENGTH */
+ FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_ERR_LINK_POINTER */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_ERR_INPUT_BUFFER */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_ERR_OUTPUT_BUFFER */
+ FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_ERR_OUTPUT_BUFFER_STARVATION */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_ERR_INTERNAL_STATE */
+ FSL_RETURN_ERROR_S, /* SAH_ERR_GENERAL_DESCRIPTOR */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_ERR_RESERVED_FIELDS */
+ FSL_RETURN_MEMORY_ERROR_S, /* SAH_ERR_DESCRIPTOR_ADDRESS */
+ FSL_RETURN_MEMORY_ERROR_S, /* SAH_ERR_LINK_ADDRESS */
+ SHA_ERROR_STATUS_CONTINUE, /* SAH_ERR_CHA */
+ SHA_ERROR_STATUS_CONTINUE /* SAH_ERR_DMA */
+};
+
+static sah_DMA_Error_Status sah_DMA_Error_Status_Array[] = {
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_DMA_NO_ERR */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_DMA_AHB_ERR */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_DMA_IP_ERR */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_DMA_PARITY_ERR */
+ FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_DMA_BOUNDRY_ERR */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_DMA_BUSY_ERR */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_DMA_RESERVED_ERR */
+ FSL_RETURN_INTERNAL_ERROR_S /* SAH_DMA_INT_ERR */
+};
+
+static sah_CHA_Error_Status sah_CHA_Error_Status_Array[] = {
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_CHA_NO_ERR */
+ FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_CHA_IP_BUF */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_CHA_ADD_ERR */
+ FSL_RETURN_BAD_MODE_S, /* SAH_CHA_MODE_ERR */
+ FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_CHA_DATA_SIZE_ERR */
+ FSL_RETURN_BAD_KEY_LENGTH_S, /* SAH_CHA_KEY_SIZE_ERR */
+ FSL_RETURN_BAD_MODE_S, /* SAH_CHA_PROC_ERR */
+ FSL_RETURN_ERROR_S, /* SAH_CHA_CTX_READ_ERR */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_CHA_INTERNAL_HW_ERR */
+ FSL_RETURN_MEMORY_ERROR_S, /* SAH_CHA_IP_BUFF_ERR */
+ FSL_RETURN_MEMORY_ERROR_S, /* SAH_CHA_OP_BUFF_ERR */
+ FSL_RETURN_BAD_KEY_PARITY_S, /* SAH_CHA_DES_KEY_ERR */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_CHA_RES */
+};
+
+#ifdef DIAG_DRV_STATUS
+
+char sah_return_text[FSL_INVALID_RETURN][MAX_RETURN_STRING_LEN] = {
+ "No error", /* FSL_RETURN_OK_S */
+ "Error", /* FSL_RETURN_ERROR_S */
+ "No resource", /* FSL_RETURN_NO_RESOURCE_S */
+ "Bad algorithm", /* FSL_RETURN_BAD_ALGORITHM_S */
+ "Bad mode", /* FSL_RETURN_BAD_MODE_S */
+ "Bad flag", /* FSL_RETURN_BAD_FLAG_S */
+ "Bad key length", /* FSL_RETURN_BAD_KEY_LENGTH_S */
+ "Bad key parity", /* FSL_RETURN_BAD_KEY_PARITY_S */
+ "Bad data length", /* FSL_RETURN_BAD_DATA_LENGTH_S */
+ "Authentication failed", /* FSL_RETURN_AUTH_FAILED_S */
+ "Memory error", /* FSL_RETURN_MEMORY_ERROR_S */
+ "Internal error", /* FSL_RETURN_INTERNAL_ERROR_S */
+ "unknown value", /* default */
+};
+
+#endif /* DIAG_DRV_STATUS */
+
+/*!
+ * This lock must be held while performing any queuing or unqueuing functions,
+ * including reading the first pointer on the queue. It also protects reading
+ * and writing the Sahara DAR register. It must be held during a read-write
+ * operation on the DAR so that the 'test-and-set' is atomic.
+ */
+os_lock_t desc_queue_lock;
+
+/*! This is the main queue for the driver. This is shared between all threads
+ * and is not protected by mutexes since the kernel is non-preemptable. */
+sah_Queue *main_queue = NULL;
+
+/* Internal Prototypes */
+sah_Head_Desc *sah_Find_With_State(sah_Queue_Status state);
+
+#ifdef DIAG_DRV_STATUS
+void sah_Log_Error(uint32_t descriptor, uint32_t error, uint32_t fault_address);
+#endif
+
+extern wait_queue_head_t *int_queue;
+
+/*!
+ * This function initialises the Queue Manager
+ *
+ * @brief Initialise the Queue Manager
+ *
+ * @return FSL_RETURN_OK_S on success; FSL_RETURN_MEMORY_ERROR_S if not
+ */
+fsl_shw_return_t sah_Queue_Manager_Init(void)
+{
+ fsl_shw_return_t ret_val = FSL_RETURN_OK_S;
+
+ desc_queue_lock = os_lock_alloc_init();
+
+ if (main_queue == NULL) {
+ /* Construct the main queue. */
+ main_queue = sah_Queue_Construct();
+
+ if (main_queue == NULL) {
+ ret_val = FSL_RETURN_MEMORY_ERROR_S;
+ }
+ } else {
+#ifdef DIAG_DRV_QUEUE
+ LOG_KDIAG
+ ("Trying to initialise the queue manager more than once.");
+#endif
+ }
+
+ return ret_val;
+}
+
+/*!
+ * This function closes the Queue Manager
+ *
+ * @brief Close the Queue Manager
+ *
+ * @return void
+ */
+void sah_Queue_Manager_Close(void)
+{
+#ifdef DIAG_DRV_QUEUE
+ if (main_queue && main_queue->count != 0) {
+ LOG_KDIAG
+ ("Trying to close the main queue when it is not empty.");
+ }
+#endif
+
+ if (main_queue) {
+ /* There is no error checking here because there is no way to handle
+ it. */
+ sah_Queue_Destroy(main_queue);
+ main_queue = NULL;
+ }
+}
+
+/*!
+ * Count the number of entries on the Queue Manager's queue
+ *
+ * @param ignore_state If non-zero, the @a state parameter is ignored.
+ * If zero, only entries matching @a state are counted.
+ * @param state State of entry to match for counting.
+ *
+ * @return Number of entries which matched criteria
+ */
+int sah_Queue_Manager_Count_Entries(int ignore_state, sah_Queue_Status state)
+{
+ int count = 0;
+ sah_Head_Desc *current_entry;
+
+ /* Start at the head */
+ current_entry = main_queue->head;
+ while (current_entry != NULL) {
+ if (ignore_state || (current_entry->status == state)) {
+ count++;
+ }
+ /* Jump to the next entry. */
+ current_entry = current_entry->next;
+ }
+
+ return count;
+}
+
+/*!
+ * This function removes an entry from the Queue Manager's queue. The entry to
+ * be removed can be anywhere in the queue.
+ *
+ * @brief Remove an entry from the Queue Manager's queue.
+ *
+ * @param entry A pointer to a sah_Head_Desc to remove from the Queue
+ * Manager's queue.
+ *
+ * @pre The #desc_queue_lock must be held before calling this function.
+ *
+ * @return void
+ */
+void sah_Queue_Manager_Remove_Entry(sah_Head_Desc * entry)
+{
+ if (entry == NULL) {
+#ifdef DIAG_DRV_QUEUE
+ LOG_KDIAG("NULL pointer input.");
+#endif
+ } else {
+ sah_Queue_Remove_Any_Entry(main_queue, entry);
+ }
+}
+
+/*!
+ * This function appends an entry to the Queue Managers queue. It primes SAHARA
+ * if this entry is the first PENDING entry in the Queue Manager's Queue.
+ *
+ * @brief Appends an entry to the Queue Manager's queue.
+ *
+ * @param entry A pointer to a sah_Head_Desc to append to the Queue
+ * Manager's queue.
+ *
+ * @pre The #desc_queue_lock may not may be held when calling this function.
+ *
+ * @return void
+ */
+void sah_Queue_Manager_Append_Entry(sah_Head_Desc * entry)
+{
+ sah_Head_Desc *current_entry;
+ os_lock_context_t int_flags;
+
+#ifdef DIAG_DRV_QUEUE
+ if (entry == NULL) {
+ LOG_KDIAG("NULL pointer input.");
+ }
+#endif
+ entry->status = SAH_STATE_PENDING;
+ os_lock_save_context(desc_queue_lock, int_flags);
+ sah_Queue_Append_Entry(main_queue, entry);
+
+ /* Prime SAHARA if the operation that was just appended is the only PENDING
+ * operation in the queue.
+ */
+ current_entry = sah_Find_With_State(SAH_STATE_PENDING);
+ if (current_entry != NULL) {
+ if (current_entry == entry) {
+ sah_Queue_Manager_Prime(entry);
+ }
+ }
+
+ os_unlock_restore_context(desc_queue_lock, int_flags);
+}
+
+/*!
+ * This function marks all entries in the Queue Manager's queue with state
+ * SAH_STATE_RESET.
+ *
+ * @brief Mark all entries with state SAH_STATE_RESET
+ *
+ * @return void
+ *
+ * @note This feature needs re-visiting
+ */
+void sah_Queue_Manager_Reset_Entries(void)
+{
+ sah_Head_Desc *current_entry = NULL;
+
+ /* Start at the head */
+ current_entry = main_queue->head;
+
+ while (current_entry != NULL) {
+ /* Set the state. */
+ current_entry->status = SAH_STATE_RESET;
+ /* Jump to the next entry. */
+ current_entry = current_entry->next;
+ }
+}
+
+/*!
+ * This function primes SAHARA for the first time or after the queue becomes
+ * empty. Queue lock must have been set by the caller of this routine.
+ *
+ * @brief Prime SAHARA.
+ *
+ * @param entry A pointer to a sah_Head_Desc to Prime SAHARA with.
+ *
+ * @return void
+ */
+void sah_Queue_Manager_Prime(sah_Head_Desc * entry)
+{
+#ifdef DIAG_DRV_QUEUE
+ LOG_KDIAG("Priming SAHARA");
+ if (entry == NULL) {
+ LOG_KDIAG("Trying to prime SAHARA with a NULL entry pointer.");
+ }
+#endif
+
+#ifndef SUBMIT_MULTIPLE_DARS
+ /* BUG FIX: state machine can transition from Done1 Busy2 directly
+ * to Idle. To fix that problem, only one DAR is being allowed on
+ * SAHARA at a time */
+ if (sah_Find_With_State(SAH_STATE_ON_SAHARA) != NULL) {
+ return;
+ }
+#endif
+
+#ifdef SAHARA_POWER_MANAGEMENT
+ /* check that dynamic power management is not asserted */
+ if (!sah_dpm_flag) {
+#endif
+
+ /* Enable the SAHARA Clocks */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA : Enabling the IPG and AHB clocks\n")
+#endif /*DIAG_DRV_IF */
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ mxc_clks_enable(SAHARA2_CLK);
+#else
+ {
+ struct clk *clk = clk_get(NULL, "sahara_clk");
+ if (!IS_ERR(clk))
+ clk_enable(clk);
+ clk_put(clk);
+ }
+#endif
+
+ /* Make sure nothing is in the DAR */
+ if (sah_HW_Read_DAR() == 0) {
+#if defined(DIAG_DRV_IF)
+ sah_Dump_Chain(&entry->desc, entry->desc.dma_addr);
+#endif /* DIAG_DRV_IF */
+
+ sah_HW_Write_DAR((entry->desc.dma_addr));
+ entry->status = SAH_STATE_ON_SAHARA;
+ }
+#ifdef DIAG_DRV_QUEUE
+ else {
+ LOG_KDIAG("DAR should be empty when Priming SAHARA");
+ }
+#endif
+#ifdef SAHARA_POWER_MANAGEMENT
+ }
+#endif
+}
+
+#ifndef SAHARA_POLL_MODE
+
+/*!
+ * Reset SAHARA, then load the next descriptor on it, if one exists
+ */
+void sah_reset_sahara_request(void)
+{
+ sah_Head_Desc *desc;
+ os_lock_context_t lock_flags;
+
+#ifdef DIAG_DRV_STATUS
+ LOG_KDIAG("Sahara required reset from tasklet, replace chip");
+#endif
+ sah_HW_Reset();
+
+ /* Now stick in a waiting request */
+ os_lock_save_context(desc_queue_lock, lock_flags);
+ if ((desc = sah_Find_With_State(SAH_STATE_PENDING))) {
+ sah_Queue_Manager_Prime(desc);
+ }
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+}
+
+/*!
+ * Post-process a descriptor chain after the hardware has finished with it.
+ *
+ * The status of the descriptor could also be checked. (for FATAL or IGNORED).
+ *
+ * @param desc_head The finished chain
+ * @param error A boolean to mark whether hardware reported error
+ *
+ * @pre The #desc_queue_lock may not be held when calling this function.
+ */
+void sah_process_finished_request(sah_Head_Desc * desc_head, unsigned error)
+{
+ os_lock_context_t lock_flags;
+
+ if (!error) {
+ desc_head->result = FSL_RETURN_OK_S;
+ } else if (desc_head->error_status == -1) {
+ /* Disaster! Sahara has faulted */
+ desc_head->result = FSL_RETURN_ERROR_S;
+ } else {
+ /* translate from SAHARA error status to fsl_shw return values */
+ desc_head->result =
+ sah_convert_error_status(desc_head->error_status);
+#ifdef DIAG_DRV_STATUS
+ sah_Log_Error(desc_head->desc.dma_addr, desc_head->error_status,
+ desc_head->fault_address);
+#endif
+ }
+
+ /* Show that the request has been processd */
+ desc_head->status = error ? SAH_STATE_FAILED : SAH_STATE_COMPLETE;
+
+ if (desc_head->uco_flags & FSL_UCO_BLOCKING_MODE) {
+
+ /* Wake up all processes on Sahara queue */
+ wake_up_interruptible(int_queue);
+
+ } else {
+ os_lock_save_context(desc_queue_lock, lock_flags);
+ sah_Queue_Append_Entry(&desc_head->user_info->result_pool,
+ desc_head);
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+
+ /* perform callback */
+ if (desc_head->uco_flags & FSL_UCO_CALLBACK_MODE) {
+ desc_head->user_info->callback(desc_head->user_info);
+ }
+ }
+} /* sah_process_finished_request */
+
+/*! Called from bottom half.
+ *
+ * @pre The #desc_queue_lock may not be held when calling this function.
+ */
+void sah_postprocess_queue(unsigned long reset_flag)
+{
+
+ sah_Head_Desc *first_entry;
+ os_lock_context_t lock_flags;
+
+ /* Disabling Sahara Clock only if the hardware is in idle state and
+ the DAR queue is empty.*/
+ os_lock_save_context(desc_queue_lock, lock_flags);
+ first_entry = sah_Find_With_State(SAH_STATE_ON_SAHARA);
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+
+ if ((first_entry == NULL) && (sah_HW_Read_Status() == SAH_EXEC_IDLE)) {
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA : Disabling the clocks\n")
+#endif /* DIAG_DRV_IF */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ mxc_clks_disable(SAHARA2_CLK);
+#else
+ {
+ struct clk *clk = clk_get(NULL, "sahara_clk");
+ if (!IS_ERR(clk))
+ clk_disable(clk);
+ clk_put(clk);
+ }
+#endif
+
+ }
+
+
+ /* if SAHARA needs to be reset, do it here. This starts a descriptor chain
+ * if one is ready also */
+ if (reset_flag) {
+ sah_reset_sahara_request();
+ }
+
+ /* now handle the descriptor chain(s) that has/have completed */
+ do {
+
+ os_lock_save_context(desc_queue_lock, lock_flags);
+
+ first_entry = main_queue->head;
+ if ((first_entry != NULL) &&
+ (first_entry->status == SAH_STATE_OFF_SAHARA)) {
+ sah_Queue_Remove_Entry(main_queue);
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+
+ sah_process_finished_request(first_entry,
+ (first_entry->
+ error_status != 0));
+ } else {
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+ break;
+ }
+ } while (1);
+
+ return;
+}
+
+#endif /* ifndef SAHARA_POLL_MODE */
+
+/*!
+ * This is a helper function for Queue Manager. This function finds the first
+ * entry in the Queue Manager's queue whose state matches the given input
+ * state. This function starts at the head of the queue and works towards the
+ * tail. If a matching entry was found, the address of the entry is returned.
+ *
+ * @brief Handle the IDLE state.
+ *
+ * @param state A sah_Queue_Status value.
+ *
+ * @pre The #desc_queue_lock must be held before calling this function.
+ *
+ * @return A pointer to a sah_Head_Desc that matches the given state.
+ * @return NULL otherwise.
+ */
+sah_Head_Desc *sah_Find_With_State(sah_Queue_Status state)
+{
+ sah_Head_Desc *current_entry = NULL;
+ sah_Head_Desc *ret_val = NULL;
+ int done_looping = FALSE;
+
+ /* Start at the head */
+ current_entry = main_queue->head;
+
+ while ((current_entry != NULL) && (done_looping == FALSE)) {
+ if (current_entry->status == state) {
+ done_looping = TRUE;
+ ret_val = current_entry;
+ }
+ /* Jump to the next entry. */
+ current_entry = current_entry->next;
+ }
+
+ return ret_val;
+} /* sah_postprocess_queue */
+
+/*!
+ * Process the value from the Sahara error status register and convert it into
+ * an FSL SHW API error code.
+ *
+ * Warning, this routine must only be called if an error exists.
+ *
+ * @param error_status The value from the error status register.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_convert_error_status(uint32_t error_status)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S; /* catchall */
+ uint8_t error_source;
+ uint8_t DMA_error_status;
+ uint8_t DMA_error_size;
+
+ /* get the error source from the error status register */
+ error_source = error_status & SAH_ERROR_MASK;
+
+ /* array size is maximum allowed by mask, so no boundary checking is
+ * needed here */
+ ret = sah_Execute_Error_Array[error_source];
+
+ /* is this one that needs additional fields checked to determine the
+ * error condition? */
+ if (ret == SHA_ERROR_STATUS_CONTINUE) {
+ /* check the DMA fields */
+ if (error_source == SAH_ERR_DMA) {
+ /* get the DMA transfer error size. If this indicates that no
+ * error was detected, something is seriously wrong */
+ DMA_error_size =
+ (error_status >> 9) & SAH_DMA_ERR_SIZE_MASK;
+ if (DMA_error_size == SAH_DMA_NO_ERR) {
+ ret = FSL_RETURN_INTERNAL_ERROR_S;
+ } else {
+ /* get DMA error status */
+ DMA_error_status = (error_status >> 12) &
+ SAH_DMA_ERR_STATUS_MASK;
+
+ /* the DMA error bits cover all the even numbers. By dividing
+ * by 2 it can be used as an index into the error array */
+ ret =
+ sah_DMA_Error_Status_Array[DMA_error_status
+ >> 1];
+ }
+ } else { /* not SAH_ERR_DMA, so must be SAH_ERR_CHA */
+ uint16_t CHA_error_status;
+ uint8_t CHA_error_source;
+
+ /* get CHA Error Source. If this indicates that no error was
+ * detected, something is seriously wrong */
+ CHA_error_source =
+ (error_status >> 28) & SAH_CHA_ERR_SOURCE_MASK;
+ if (CHA_error_source == SAH_CHA_NO_ERROR) {
+ ret = FSL_RETURN_INTERNAL_ERROR_S;
+ } else {
+ uint32_t mask = 1;
+ uint32_t count = 0;
+
+ /* get CHA Error Status */
+ CHA_error_status = (error_status >> 16) &
+ SAH_CHA_ERR_STATUS_MASK;
+
+ /* If more than one bit is set (which shouldn't happen), only
+ * the first will be captured */
+ if (CHA_error_status != 0) {
+ count = 1;
+ while (CHA_error_status != mask) {
+ ++count;
+ mask <<= 1;
+ }
+ }
+
+ ret = sah_CHA_Error_Status_Array[count];
+ }
+ }
+ }
+
+ return ret;
+}
+
+fsl_shw_return_t sah_convert_op_status(uint32_t op_status)
+{
+ unsigned op_source = (op_status >> 28) & 0x7;
+ unsigned op_detail = op_status & 0x3f;
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ switch (op_source) {
+ case 1: /* SKHA */
+ /* Can't this have "ICV" error from CCM ?? */
+ break;
+ case 2: /* MDHA */
+ if (op_detail == 1) {
+ ret = FSL_RETURN_AUTH_FAILED_S;
+ }
+ break;
+ case 3: /* RNGA */
+ /* Self-test and Compare errors... what to do? */
+ break;
+ case 4: /* PKHA */
+ switch (op_detail) {
+ case 0x01:
+ ret = FSL_RETURN_PRIME_S;
+ break;
+ case 0x02:
+ ret = FSL_RETURN_NOT_PRIME_S;
+ break;
+ case 0x04:
+ ret = FSL_RETURN_POINT_AT_INFINITY_S;
+ break;
+ case 0x08:
+ ret = FSL_RETURN_POINT_NOT_AT_INFINITY_S;
+ break;
+ case 0x10:
+ ret = FSL_RETURN_GCD_IS_ONE_S;
+ break;
+ case 0x20:
+ ret = FSL_RETURN_GCD_IS_NOT_ONE_S;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+#ifdef DIAG_DRV_STATUS
+
+/*!
+ * This function logs the diagnostic information for the given error and
+ * descriptor address. Only used for diagnostic purposes.
+ *
+ * @brief (debug only) Log a description of hardware-detected error.
+ *
+ * @param descriptor The descriptor address that caused the error
+ * @param error The SAHARA error code
+ * @param fault_address Value from the Fault address register
+ *
+ * @return void
+ */
+void sah_Log_Error(uint32_t descriptor, uint32_t error, uint32_t fault_address)
+{
+ char *source_text; /* verbose error source from register */
+ char *address; /* string buffer for descriptor address */
+ char *error_log; /* the complete logging message */
+ char *cha_log = NULL; /* string buffer for descriptor address */
+ char *dma_log = NULL; /* string buffer for descriptor address */
+
+ uint16_t cha_error = 0;
+ uint16_t dma_error = 0;
+
+ uint8_t error_source;
+ sah_Execute_Error return_code;
+
+ /* log error code and descriptor address */
+ error_source = error & SAH_ERROR_MASK;
+ return_code = sah_Execute_Error_Array[error_source];
+
+ source_text = os_alloc_memory(64, GFP_KERNEL);
+
+ switch (error_source) {
+ case SAH_ERR_HEADER:
+ sprintf(source_text, "%s", "Header is not valid");
+ break;
+
+ case SAH_ERR_DESC_LENGTH:
+ sprintf(source_text, "%s",
+ "Descriptor length not equal to sum of link lengths");
+ break;
+
+ case SAH_ERR_DESC_POINTER:
+ sprintf(source_text, "%s", "Length or pointer "
+ "field is zero while the other is non-zero");
+ break;
+
+ case SAH_ERR_LINK_LENGTH:
+ /* note that the Sahara Block Guide 2.7 has an invalid explaination
+ * of this. It only happens when a link length is zero */
+ sprintf(source_text, "%s", "A data length is a link is zero");
+ break;
+
+ case SAH_ERR_LINK_POINTER:
+ sprintf(source_text, "%s",
+ "The data pointer in a link is zero");
+ break;
+
+ case SAH_ERR_INPUT_BUFFER:
+ sprintf(source_text, "%s", "Input Buffer reported an overflow");
+ break;
+
+ case SAH_ERR_OUTPUT_BUFFER:
+ sprintf(source_text, "%s",
+ "Output Buffer reported an underflow");
+ break;
+
+ case SAH_ERR_OUTPUT_BUFFER_STARVATION:
+ sprintf(source_text, "%s", "Incorrect data in output "
+ "buffer after CHA has signalled 'done'");
+ break;
+
+ case SAH_ERR_INTERNAL_STATE:
+ sprintf(source_text, "%s", "Internal Hardware Failure");
+ break;
+
+ case SAH_ERR_GENERAL_DESCRIPTOR:
+ sprintf(source_text, "%s",
+ "Current Descriptor was not legal, but cause is unknown");
+ break;
+
+ case SAH_ERR_RESERVED_FIELDS:
+ sprintf(source_text, "%s",
+ "Reserved pointer field is non-zero");
+ break;
+
+ case SAH_ERR_DESCRIPTOR_ADDRESS:
+ sprintf(source_text, "%s",
+ "Descriptor address not word aligned");
+ break;
+
+ case SAH_ERR_LINK_ADDRESS:
+ sprintf(source_text, "%s", "Link address not word aligned");
+ break;
+
+ case SAH_ERR_CHA:
+ sprintf(source_text, "%s", "CHA Error");
+ {
+ char *cha_module = os_alloc_memory(5, GFP_KERNEL);
+ char *cha_text = os_alloc_memory(45, GFP_KERNEL);
+
+ cha_error = (error >> 28) & SAH_CHA_ERR_SOURCE_MASK;
+
+ switch (cha_error) {
+ case SAH_CHA_SKHA_ERROR:
+ sprintf(cha_module, "%s", "SKHA");
+ break;
+
+ case SAH_CHA_MDHA_ERROR:
+ sprintf(cha_module, "%s", "MDHA");
+ break;
+
+ case SAH_CHA_RNG_ERROR:
+ sprintf(cha_module, "%s", "RNG ");
+ break;
+
+ case SAH_CHA_PKHA_ERROR:
+ sprintf(cha_module, "%s", "PKHA");
+ break;
+
+ case SAH_CHA_NO_ERROR:
+ /* can't happen */
+ /* no break */
+ default:
+ sprintf(cha_module, "%s", "????");
+ break;
+ }
+
+ cha_error = (error >> 16) & SAH_CHA_ERR_STATUS_MASK;
+
+ /* Log CHA Error Status */
+ switch (cha_error) {
+ case SAH_CHA_IP_BUF:
+ sprintf(cha_text, "%s",
+ "Non-empty input buffer when done");
+ break;
+
+ case SAH_CHA_ADD_ERR:
+ sprintf(cha_text, "%s", "Illegal address");
+ break;
+
+ case SAH_CHA_MODE_ERR:
+ sprintf(cha_text, "%s", "Illegal mode");
+ break;
+
+ case SAH_CHA_DATA_SIZE_ERR:
+ sprintf(cha_text, "%s", "Illegal data size");
+ break;
+
+ case SAH_CHA_KEY_SIZE_ERR:
+ sprintf(cha_text, "%s", "Illegal key size");
+ break;
+
+ case SAH_CHA_PROC_ERR:
+ sprintf(cha_text, "%s",
+ "Mode/Context/Key written during processing");
+ break;
+
+ case SAH_CHA_CTX_READ_ERR:
+ sprintf(cha_text, "%s",
+ "Context read during processing");
+ break;
+
+ case SAH_CHA_INTERNAL_HW_ERR:
+ sprintf(cha_text, "%s", "Internal hardware");
+ break;
+
+ case SAH_CHA_IP_BUFF_ERR:
+ sprintf(cha_text, "%s",
+ "Input buffer not enabled or underflow");
+ break;
+
+ case SAH_CHA_OP_BUFF_ERR:
+ sprintf(cha_text, "%s",
+ "Output buffer not enabled or overflow");
+ break;
+
+ case SAH_CHA_DES_KEY_ERR:
+ sprintf(cha_text, "%s", "DES key parity error");
+ break;
+
+ case SAH_CHA_RES:
+ sprintf(cha_text, "%s", "Reserved");
+ break;
+
+ case SAH_CHA_NO_ERR:
+ /* can't happen */
+ /* no break */
+ default:
+ sprintf(cha_text, "%s", "Unknown error");
+ break;
+ }
+
+ cha_log = os_alloc_memory(90, GFP_KERNEL);
+ sprintf(cha_log,
+ " Module %s encountered the error: %s.",
+ cha_module, cha_text);
+
+ os_free_memory(cha_module);
+ os_free_memory(cha_text);
+
+ {
+ uint32_t mask = 1;
+ uint32_t count = 0;
+
+ if (cha_error != 0) {
+ count = 1;
+ while (cha_error != mask) {
+ ++count;
+ mask <<= 1;
+ }
+ }
+
+ return_code = sah_CHA_Error_Status_Array[count];
+ }
+ cha_error = 1;
+ }
+ break;
+
+ case SAH_ERR_DMA:
+ sprintf(source_text, "%s", "DMA Error");
+ {
+ char *dma_direction = os_alloc_memory(6, GFP_KERNEL);
+ char *dma_size = os_alloc_memory(14, GFP_KERNEL);
+ char *dma_text = os_alloc_memory(250, GFP_KERNEL);
+
+ if ((dma_direction == NULL) || (dma_size == NULL) ||
+ (dma_text == NULL)) {
+ LOG_KDIAG
+ ("No memory allocated for DMA debug messages\n");
+ }
+
+ /* log DMA error direction */
+ sprintf(dma_direction, "%s",
+ (((error >> 8) & SAH_DMA_ERR_DIR_MASK) == 1) ?
+ "read" : "write");
+
+ /* log the size of the DMA transfer error */
+ dma_error = (error >> 9) & SAH_DMA_ERR_SIZE_MASK;
+ switch (dma_error) {
+ case SAH_DMA_SIZE_BYTE:
+ sprintf(dma_size, "%s", "byte");
+ break;
+
+ case SAH_DMA_SIZE_HALF_WORD:
+ sprintf(dma_size, "%s", "half-word");
+ break;
+
+ case SAH_DMA_SIZE_WORD:
+ sprintf(dma_size, "%s", "word");
+ break;
+
+ case SAH_DMA_SIZE_RES:
+ sprintf(dma_size, "%s", "reserved size");
+ break;
+
+ default:
+ sprintf(dma_size, "%s", "unknown size");
+ break;
+ }
+
+ /* log DMA error status */
+ dma_error = (error >> 12) & SAH_DMA_ERR_STATUS_MASK;
+ switch (dma_error) {
+ case SAH_DMA_NO_ERR:
+ sprintf(dma_text, "%s", "No DMA Error Code");
+ break;
+
+ case SAH_DMA_AHB_ERR:
+ sprintf(dma_text, "%s",
+ "AHB terminated a bus cycle with an error");
+ break;
+
+ case SAH_DMA_IP_ERR:
+ sprintf(dma_text, "%s",
+ "Internal IP bus cycle was terminated with an "
+ "error termination. This would likely be "
+ "caused by a descriptor length being too "
+ "large, and thus accessing an illegal "
+ "internal address. Verify the length field "
+ "of the current descriptor");
+ break;
+
+ case SAH_DMA_PARITY_ERR:
+ sprintf(dma_text, "%s",
+ "Parity error detected on DMA command from "
+ "Descriptor Decoder. Cause is likely to be "
+ "internal hardware fault");
+ break;
+
+ case SAH_DMA_BOUNDRY_ERR:
+ sprintf(dma_text, "%s",
+ "DMA was requested to cross a 256 byte "
+ "internal address boundary. Cause is likely a "
+ "descriptor length being too large, thus "
+ "accessing two different internal hardware "
+ "blocks");
+ break;
+
+ case SAH_DMA_BUSY_ERR:
+ sprintf(dma_text, "%s",
+ "Descriptor Decoder has made a DMA request "
+ "while the DMA controller is busy. Cause is "
+ "likely due to hardware fault");
+ break;
+
+ case SAH_DMA_RESERVED_ERR:
+ sprintf(dma_text, "%s", "Reserved");
+ break;
+
+ case SAH_DMA_INT_ERR:
+ sprintf(dma_text, "%s",
+ "Internal DMA hardware error detected. The "
+ "DMA controller has detected an internal "
+ "condition which should never occur");
+ break;
+
+ default:
+ sprintf(dma_text, "%s",
+ "Unknown DMA Error Status Code");
+ break;
+ }
+
+ return_code =
+ sah_DMA_Error_Status_Array[dma_error >> 1];
+ dma_error = 1;
+
+ dma_log = os_alloc_memory(320, GFP_KERNEL);
+ sprintf(dma_log,
+ " Occurred during a %s operation of a %s transfer: %s.",
+ dma_direction, dma_size, dma_text);
+
+ os_free_memory(dma_direction);
+ os_free_memory(dma_size);
+ os_free_memory(dma_text);
+ }
+ break;
+
+ case SAH_ERR_NONE:
+ default:
+ sprintf(source_text, "%s", "Unknown Error Code");
+ break;
+ }
+
+ address = os_alloc_memory(35, GFP_KERNEL);
+
+ /* convert error & descriptor address to strings */
+ if (dma_error) {
+ sprintf(address, "Fault address is 0x%08x", fault_address);
+ } else {
+ sprintf(address, "Descriptor bus address is 0x%08x",
+ descriptor);
+ }
+
+ if (return_code > FSL_INVALID_RETURN) {
+ return_code = FSL_INVALID_RETURN;
+ }
+
+ error_log = os_alloc_memory(250, GFP_KERNEL);
+
+ /* construct final log message */
+ sprintf(error_log, "Error source = 0x%08x. Return = %s. %s. %s.",
+ error, sah_return_text[return_code], address, source_text);
+
+ os_free_memory(source_text);
+ os_free_memory(address);
+
+ /* log standard messages */
+ LOG_KDIAG(error_log);
+ os_free_memory(error_log);
+
+ /* add additional information if available */
+ if (cha_error) {
+ LOG_KDIAG(cha_log);
+ os_free_memory(cha_log);
+ }
+
+ if (dma_error) {
+ LOG_KDIAG(dma_log);
+ os_free_memory(dma_log);
+ }
+
+ return;
+} /* sah_Log_Error */
+
+#endif /* DIAG_DRV_STATUS */
+
+/* End of sah_queue_manager.c */
diff --git a/drivers/mxc/security/sahara2/sah_status_manager.c b/drivers/mxc/security/sahara2/sah_status_manager.c
new file mode 100644
index 000000000000..f881b47591c2
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_status_manager.c
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+* @file sah_status_manager.c
+*
+* @brief Status Manager Function
+*
+* This file contains the function which processes the Sahara status register
+* during an interrupt.
+*
+* This file does not need porting.
+*/
+
+#include "portable_os.h"
+
+#include <sah_status_manager.h>
+#include <sah_hardware_interface.h>
+#include <sah_queue_manager.h>
+#include <sah_memory_mapper.h>
+#include <sah_kernel.h>
+
+#if defined(DIAG_DRV_INTERRUPT) && defined(DIAG_DURING_INTERRUPT)
+#include <diagnostic.h>
+#endif
+
+/*! Compile-time flag to count various interrupt types. */
+#define DIAG_INT_COUNT
+
+/*!
+ * Number of interrupts processed with Done1Done2 status. Updates to this
+ * value should only be done in interrupt processing.
+ */
+uint32_t done1_count;
+
+/*!
+ * Number of interrupts processed with Done1Busy2 status. Updates to this
+ * value should only be done in interrupt processing.
+ */
+uint32_t done1busy2_count;
+
+/*!
+ * Number of interrupts processed with Done1Done2 status. Updates to this
+ * value should only be done in interrupt processing.
+ */
+uint32_t done1done2_count;
+
+/*!
+ * the dynameic power management flag is false when power management is not
+ * asserted and true when dpm is.
+ */
+#ifdef SAHARA_POWER_MANAGEMENT
+bool sah_dpm_flag = FALSE;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+static int sah_dpm_suspend(struct device *dev, uint32_t state, uint32_t level);
+static int sah_dpm_resume(struct device *dev, uint32_t level);
+#else
+static int sah_dpm_suspend(struct platform_device *dev, pm_message_t state);
+static int sah_dpm_resume(struct platform_device *dev);
+#endif
+#endif
+
+#ifndef SAHARA_POLL_MODE
+/*!
+*******************************************************************************
+* This functionx processes the status register of the Sahara, updates the state
+* of the finished queue entry, and then tries to find more work for Sahara to
+* do.
+*
+* @brief The bulk of the interrupt handling code.
+*
+* @param hw_status The status register of Sahara at time of interrupt.
+* The Clear interrupt bit is already handled by this
+* register read prior to entry into this function.
+* @return void
+*/
+unsigned long sah_Handle_Interrupt(sah_Execute_Status hw_status)
+{
+ unsigned long reset_flag = 0; /* assume no SAHARA reset needed */
+ os_lock_context_t lock_flags;
+ sah_Head_Desc *current_entry;
+
+ /* HW status at time of interrupt */
+ sah_Execute_Status state = hw_status & SAH_EXEC_STATE_MASK;
+
+ do {
+ uint32_t dar;
+
+#ifdef DIAG_INT_COUNT
+ if (state == SAH_EXEC_DONE1) {
+ done1_count++;
+ } else if (state == SAH_EXEC_DONE1_BUSY2) {
+ done1busy2_count++;
+ } else if (state == SAH_EXEC_DONE1_DONE2) {
+ done1done2_count++;
+ }
+#endif
+
+ /* if the first entry on sahara has completed... */
+ if ((state & SAH_EXEC_DONE1_BIT) ||
+ (state == SAH_EXEC_ERROR1)) {
+ /* lock queue while searching */
+ os_lock_save_context(desc_queue_lock, lock_flags);
+ current_entry =
+ sah_Find_With_State(SAH_STATE_ON_SAHARA);
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+
+ /* an active descriptor was not found */
+ if (current_entry == NULL) {
+ /* change state to avoid an infinite loop (possible if
+ * state is SAH_EXEC_DONE1_BUSY2 first time into loop) */
+ hw_status = SAH_EXEC_IDLE;
+#if defined(DIAG_DRV_INTERRUPT) && defined(DIAG_DURING_INTERRUPT)
+ LOG_KDIAG
+ ("Interrupt received with nothing on queue.");
+#endif
+ } else {
+ /* SAHARA has completed its work on this descriptor chain */
+ current_entry->status = SAH_STATE_OFF_SAHARA;
+
+ if (state == SAH_EXEC_ERROR1) {
+ if (hw_status & STATUS_ERROR) {
+ /* Gather extra diagnostic information */
+ current_entry->fault_address =
+ sah_HW_Read_Fault_Address();
+ /* Read this last - it clears the error */
+ current_entry->error_status =
+ sah_HW_Read_Error_Status();
+ current_entry->op_status = 0;
+#ifdef FSL_HAVE_SAHARA4
+ } else {
+ current_entry->op_status =
+ sah_HW_Read_Op_Status();
+ current_entry->error_status = 0;
+#endif
+ }
+
+ } else {
+ /* indicate that no errors were found with descriptor
+ * chain 1 */
+ current_entry->error_status = 0;
+ current_entry->op_status = 0;
+
+ /* is there a second, successfully, completed descriptor
+ * chain? (done1/error2 processing is handled later) */
+ if (state == SAH_EXEC_DONE1_DONE2) {
+ os_lock_save_context
+ (desc_queue_lock,
+ lock_flags);
+ current_entry =
+ sah_Find_With_State
+ (SAH_STATE_ON_SAHARA);
+ os_unlock_restore_context
+ (desc_queue_lock,
+ lock_flags);
+
+ if (current_entry == NULL) {
+#if defined(DIAG_DRV_INTERRUPT) && defined(DIAG_DURING_INTERRUPT)
+ LOG_KDIAG
+ ("Done1_Done2 Interrupt received with "
+ "one entry on queue.");
+#endif
+ } else {
+ /* indicate no errors in descriptor chain 2 */
+ current_entry->
+ error_status = 0;
+ current_entry->status =
+ SAH_STATE_OFF_SAHARA;
+ }
+ }
+ }
+ }
+
+#ifdef SAHARA_POWER_MANAGEMENT
+ /* check dynamic power management is not asserted */
+ if (!sah_dpm_flag) {
+#endif
+ do {
+ /* protect DAR and main_queue */
+ os_lock_save_context(desc_queue_lock,
+ lock_flags);
+ dar = sah_HW_Read_DAR();
+ /* check if SAHARA has space for another descriptor. SAHARA
+ * only accepts up to the DAR queue size number of DAR
+ * entries, after that 'dar' will not be zero until the
+ * pending interrupt is serviced */
+ if (dar == 0) {
+ current_entry =
+ sah_Find_With_State
+ (SAH_STATE_PENDING);
+ if (current_entry != NULL) {
+#ifndef SUBMIT_MULTIPLE_DARS
+ /* BUG FIX: state machine can transition from Done1
+ * Busy2 directly to Idle. To fix that problem,
+ * only one DAR is being allowed on SAHARA at a
+ * time. If a high level interrupt has happened,
+ * there could * be an active descriptor chain */
+ if (sah_Find_With_State
+ (SAH_STATE_ON_SAHARA)
+ == NULL) {
+#endif
+#if defined(DIAG_DRV_IF) && defined(DIAG_DURING_INTERRUPT)
+ sah_Dump_Chain
+ (&current_entry->
+ desc,
+ current_entry->
+ desc.
+ dma_addr);
+#endif /* DIAG_DRV_IF */
+ sah_HW_Write_DAR
+ (current_entry->
+ desc.
+ dma_addr);
+ current_entry->
+ status =
+ SAH_STATE_ON_SAHARA;
+#ifndef SUBMIT_MULTIPLE_DARS
+ }
+ current_entry = NULL; /* exit loop */
+#endif
+ }
+ }
+ os_unlock_restore_context
+ (desc_queue_lock, lock_flags);
+ } while ((dar == 0) && (current_entry != NULL));
+#ifdef SAHARA_POWER_MANAGEMENT
+ } /* sah_device_power_manager */
+#endif
+ } else {
+ if (state == SAH_EXEC_FAULT) {
+ sah_Head_Desc *previous_entry; /* point to chain 1 */
+ /* Address of request when fault occured */
+ uint32_t bad_dar = sah_HW_Read_IDAR();
+
+ reset_flag = 1; /* SAHARA needs to be reset */
+
+ /* get first of possible two descriptor chain that was
+ * on SAHARA */
+ os_lock_save_context(desc_queue_lock,
+ lock_flags);
+ previous_entry =
+ sah_Find_With_State(SAH_STATE_ON_SAHARA);
+ os_unlock_restore_context(desc_queue_lock,
+ lock_flags);
+
+ /* if it exists, continue processing the fault */
+ if (previous_entry) {
+ /* assume this chain didn't complete correctly */
+ previous_entry->error_status = -1;
+ previous_entry->status =
+ SAH_STATE_OFF_SAHARA;
+
+ /* get the second descriptor chain */
+ os_lock_save_context(desc_queue_lock,
+ lock_flags);
+ current_entry =
+ sah_Find_With_State
+ (SAH_STATE_ON_SAHARA);
+ os_unlock_restore_context
+ (desc_queue_lock, lock_flags);
+
+ /* if it exists, continue processing both chains */
+ if (current_entry) {
+ /* assume this chain didn't complete correctly */
+ current_entry->error_status =
+ -1;
+ current_entry->status =
+ SAH_STATE_OFF_SAHARA;
+
+ /* now see if either can be identified as the one
+ * in progress when the fault occured */
+ if (current_entry->desc.
+ dma_addr == bad_dar) {
+ /* the second descriptor chain was active when the
+ * fault occured, so the first descriptor chain
+ * was successfull */
+ previous_entry->
+ error_status = 0;
+ } else {
+ if (previous_entry->
+ desc.dma_addr ==
+ bad_dar) {
+ /* if the first chain was in progress when the
+ * fault occured, the second has not yet been
+ * touched, so reset it to PENDING */
+ current_entry->
+ status =
+ SAH_STATE_PENDING;
+ }
+ }
+ }
+ }
+#if defined(DIAG_DRV_INTERRUPT) && defined(DIAG_DURING_INTERRUPT)
+ } else {
+ /* shouldn't ever get here */
+ if (state == SAH_EXEC_BUSY) {
+ LOG_KDIAG
+ ("Got Sahara interrupt in Busy state");
+ } else {
+ if (state == SAH_EXEC_IDLE) {
+ LOG_KDIAG
+ ("Got Sahara interrupt in Idle state");
+ } else {
+ LOG_KDIAG
+ ("Got Sahara interrupt in unknown state");
+ }
+ }
+#endif
+ }
+ }
+
+ /* haven't handled the done1/error2 (the error 2 part), so setup to
+ * do that now. Otherwise, exit loop */
+ state = (state == SAH_EXEC_DONE1_ERROR2) ?
+ SAH_EXEC_ERROR1 : SAH_EXEC_IDLE;
+
+ /* Keep going while further status is available. */
+ } while (state == SAH_EXEC_ERROR1);
+
+ return reset_flag;
+}
+
+#endif /* ifndef SAHARA_POLL_MODE */
+
+#ifdef SAHARA_POLL_MODE
+/*!
+*******************************************************************************
+* Submits descriptor chain to SAHARA, polls on SAHARA for completion, process
+* results, and dephysicalizes chain
+*
+* @brief Handle poll mode.
+*
+* @param entry Virtual address of a physicalized chain
+*
+* @return 0 this function is always successful
+*/
+
+unsigned long sah_Handle_Poll(sah_Head_Desc * entry)
+{
+ sah_Execute_Status hw_status; /* Sahara's status register */
+ os_lock_context_t lock_flags;
+
+ /* lock SARAHA */
+ os_lock_save_context(desc_queue_lock, lock_flags);
+
+#ifdef SAHARA_POWER_MANAGEMENT
+ /* check if the dynamic power management is asserted */
+ if (sah_dpm_flag) {
+ /* return that request failed to be processed */
+ entry->result = FSL_RETURN_ERROR_S;
+ entry->fault_address = 0xBAD;
+ entry->op_status= 0xBAD;
+ entry->error_status = 0xBAD;
+ } else {
+#endif /* SAHARA_POWER_MANAGEMENT */
+
+#if defined(DIAG_DRV_IF)
+ sah_Dump_Chain(&entry->desc, entry->desc.dma_addr);
+#endif /* DIAG_DRV_IF */
+ /* Nothing can be in the dar if we got the lock */
+ sah_HW_Write_DAR((uint32_t) (entry->desc.dma_addr));
+
+ /* Wait for SAHARA to finish with this entry */
+ hw_status = sah_Wait_On_Sahara();
+
+ /* if entry completed successfully, mark it as such */
+ /**** HARDWARE ERROR WORK AROUND (hw_status == SAH_EXEC_IDLE) *****/
+ if (
+#ifndef SUBMIT_MULTIPLE_DARS
+ (hw_status == SAH_EXEC_IDLE) || (hw_status == SAH_EXEC_DONE1_BUSY2) || /* should not happen */
+#endif
+ (hw_status == SAH_EXEC_DONE1)
+ ) {
+ entry->error_status = 0;
+ entry->result = FSL_RETURN_OK_S;
+ } else {
+ /* SAHARA is reporting an error with entry */
+ if (hw_status == SAH_EXEC_ERROR1) {
+ /* Gather extra diagnostic information */
+ entry->fault_address =
+ sah_HW_Read_Fault_Address();
+ /* Read this register last - it clears the error */
+ entry->error_status =
+ sah_HW_Read_Error_Status();
+ entry->op_status = 0;
+ /* translate from SAHARA error status to fsl_shw return values */
+ entry->result =
+ sah_convert_error_status(entry->
+ error_status);
+#ifdef DIAG_DRV_STATUS
+ sah_Log_Error(entry->op_status,
+ entry->error_status,
+ entry->fault_address);
+#endif
+ } else if (hw_status == SAH_EXEC_OPSTAT1) {
+ entry->op_status = sah_HW_Read_Op_Status();
+ entry->error_status = 0;
+ entry->result =
+ sah_convert_op_status(op_status);
+ } else {
+ /* SAHARA entered FAULT state (or something bazaar has
+ * happened) */
+ pr_debug
+ ("Sahara: hw_status = 0x%x; Stat: 0x%08x; IDAR: 0x%08x; "
+ "CDAR: 0x%08x; FltAdr: 0x%08x; Estat: 0x%08x\n",
+ hw_status, sah_HW_Read_Status(),
+ sah_HW_Read_IDAR(), sah_HW_Read_CDAR(),
+ sah_HW_Read_Fault_Address(),
+ sah_HW_Read_Error_Status());
+#ifdef DIAG_DRV_IF
+ {
+ int old_level = console_loglevel;
+ console_loglevel = 8;
+ sah_Dump_Chain(&(entry->desc),
+ entry->desc.dma_addr);
+ console_loglevel = old_level;
+ }
+#endif
+
+ entry->error_status = -1;
+ entry->result = FSL_RETURN_ERROR_S;
+ sah_HW_Reset();
+ }
+ }
+#ifdef SAHARA_POWER_MANAGEMENT
+ }
+#endif
+
+ if (!(entry->uco_flags & FSL_UCO_BLOCKING_MODE)) {
+ /* put it in results pool to allow get_results to work */
+ sah_Queue_Append_Entry(&entry->user_info->result_pool, entry);
+ if (entry->uco_flags & FSL_UCO_CALLBACK_MODE) {
+ /* invoke callback */
+ entry->user_info->callback(entry->user_info);
+ }
+ } else {
+ /* convert the descriptor link back to virtual memory, mark dirty pages
+ * if they are from user mode, and release the page cache for user
+ * pages
+ */
+ entry = sah_DePhysicalise_Descriptors(entry);
+ }
+
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+
+ return 0;
+}
+
+#endif /* SAHARA_POLL_MODE */
+
+/******************************************************************************
+* The following is the implementation of the Dynamic Power Management
+* functionality.
+******************************************************************************/
+#ifdef SAHARA_POWER_MANAGEMENT
+
+static bool sah_dpm_init_flag;
+
+/* dynamic power management information for the sahara driver */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+static struct device_driver sah_dpm_driver = {
+ .name = "sahara_",
+ .bus = &platform_bus_type,
+#else
+static struct platform_driver sah_dpm_driver = {
+ .driver.name = "sahara_",
+ .driver.bus = &platform_bus_type,
+#endif
+ .suspend = sah_dpm_suspend,
+ .resume = sah_dpm_resume
+};
+
+/* dynamic power management information for the sahara HW device */
+static struct platform_device sah_dpm_device = {
+ .name = "sahara_",
+ .id = 1,
+};
+
+/*!
+*******************************************************************************
+* Initilaizes the dynamic power managment functionality
+*
+* @brief Initialization of the Dynamic Power Management functionality
+*
+* @return 0 = success; failed otherwise
+*/
+int sah_dpm_init()
+{
+ int status;
+
+ /* dpm is not asserted */
+ sah_dpm_flag = FALSE;
+
+ /* register the driver to the kernel */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ status = os_register_to_driver(&sah_dpm_driver);
+#else
+ status = os_register_to_driver(&sah_dpm_driver.driver);
+#endif
+
+ if (status == 0) {
+ /* register a single sahara chip */
+ /*status = platform_device_register(&sah_dpm_device); */
+ status = os_register_a_device(&sah_dpm_device);
+
+ /* if something went awry, unregister the driver */
+ if (status != 0) {
+ /*driver_unregister(&sah_dpm_driver); */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ os_unregister_from_driver(&sah_dpm_driver);
+#else
+ os_unregister_from_driver(&sah_dpm_driver.driver);
+#endif
+ sah_dpm_init_flag = FALSE;
+ } else {
+ /* if everything went okay, flag that life is good */
+ sah_dpm_init_flag = TRUE;
+ }
+ }
+
+ /* let the kernel know how it went */
+ return status;
+
+}
+
+/*!
+*******************************************************************************
+* Unregister the dynamic power managment functionality
+*
+* @brief Unregister the Dynamic Power Management functionality
+*
+*/
+void sah_dpm_close()
+{
+ /* if dynamic power management was initilaized, kill it */
+ if (sah_dpm_init_flag == TRUE) {
+ /*driver_unregister(&sah_dpm_driver); */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ os_unregister_from_driver(&sah_dpm_driver);
+#else
+ os_unregister_from_driver(&sah_dpm_driver.driver);
+#endif
+ /*platform_device_register(&sah_dpm_device); */
+ os_unregister_a_device(&sah_dpm_device);
+ }
+}
+
+/*!
+*******************************************************************************
+* Callback routine defined by the Linux Device Model / Dynamic Power management
+* extension. It sets a global flag to disallow the driver to enter queued items
+* into Sahara's DAR.
+*
+* It allows the current active descriptor chains to complete before it returns
+*
+* @brief Suspends the driver
+*
+* @param dev contains device information
+* @param state contains state information
+* @param level level of shutdown
+*
+* @return 0 = success; failed otherwise
+*/
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+static int sah_dpm_suspend(struct device *dev, uint32_t state, uint32_t level)
+#else
+static int sah_dpm_suspend(struct platform_device *dev, pm_message_t state)
+#endif
+{
+ sah_Head_Desc *entry = NULL;
+ os_lock_context_t lock_flags;
+ int error_code = 0;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ switch (level) {
+ case SUSPEND_DISABLE:
+ /* Assert dynamic power management. This stops the driver from
+ * entering queued requests to Sahara */
+ sah_dpm_flag = TRUE;
+ break;
+
+ case SUSPEND_SAVE_STATE:
+ break;
+
+ case SUSPEND_POWER_DOWN:
+ /* hopefully between the DISABLE call and this one, the outstanding
+ * work Sahara was doing complete. this checks (and waits) for
+ * those entries that were already active on Sahara to complete */
+ /* lock queue while searching */
+ os_lock_save_context(desc_queue_lock, lock_flags);
+ do {
+ entry = sah_Find_With_State(SAH_STATE_ON_SAHARA);
+ } while (entry != NULL);
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+
+ /* now we kill the clock so the control circuitry isn't sucking
+ * any power */
+ mxc_clks_disable(SAHARA2_CLK);
+ break;
+ }
+#else
+ /* Assert dynamic power management. This stops the driver from
+ * entering queued requests to Sahara */
+ sah_dpm_flag = TRUE;
+
+ /* Now wait for any outstanding work Sahara was doing to complete.
+ * this checks (and waits) for
+ * those entries that were already active on Sahara to complete */
+ do {
+ /* lock queue while searching */
+ os_lock_save_context(desc_queue_lock, lock_flags);
+ entry = sah_Find_With_State(SAH_STATE_ON_SAHARA);
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+ } while (entry != NULL);
+
+ /* now we kill the clock so the control circuitry isn't sucking
+ * any power */
+ {
+ struct clk *clk = clk_get(NULL, "sahara_clk");
+ if (IS_ERR(clk))
+ error_code = PTR_ERR(clk);
+ else
+ clk_disable(clk);
+ }
+#endif
+
+ return error_code;
+}
+
+/*!
+*******************************************************************************
+* Callback routine defined by the Linux Device Model / Dynamic Power management
+* extension. It cleears a global flag to allow the driver to enter queued items
+* into Sahara's DAR.
+*
+* It primes the mechanism to start depleting the queue
+*
+* @brief Resumes the driver
+*
+* @param dev contains device information
+* @param level level of resumption
+*
+* @return 0 = success; failed otherwise
+*/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+static int sah_dpm_resume(struct device *dev, uint32_t level)
+#else
+static int sah_dpm_resume(struct platform_device *dev)
+#endif
+{
+ sah_Head_Desc *entry = NULL;
+ os_lock_context_t lock_flags;
+ int error_code = 0;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ switch (level) {
+ case RESUME_POWER_ON:
+ /* enable Sahara's clock */
+ mxc_clks_enable(SAHARA2_CLK);
+ break;
+
+ case RESUME_RESTORE_STATE:
+ break;
+
+ case RESUME_ENABLE:
+ /* Disable dynamic power managment. This allows the driver to put
+ * entries into Sahara's DAR */
+ sah_dpm_flag = FALSE;
+
+ /* find a pending entry to prime the pump */
+ os_lock_save_context(desc_queue_lock, lock_flags);
+ entry = sah_Find_With_State(SAH_STATE_PENDING);
+ if (entry != NULL) {
+ sah_Queue_Manager_Prime(entry);
+ }
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+ break;
+ }
+#else
+ {
+ /* enable Sahara's clock */
+ struct clk *clk = clk_get(NULL, "sahara_clk");
+
+ if (IS_ERR(clk))
+ error_code = PTR_ERR(clk);
+ else
+ clk_disable(clk);
+ }
+ sah_dpm_flag = FALSE;
+
+ /* find a pending entry to prime the pump */
+ os_lock_save_context(desc_queue_lock, lock_flags);
+ entry = sah_Find_With_State(SAH_STATE_PENDING);
+ if (entry != NULL) {
+ sah_Queue_Manager_Prime(entry);
+ }
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+#endif
+ return error_code;
+}
+
+#endif /* SAHARA_POWER_MANAGEMENT */
diff --git a/drivers/mxc/security/sahara2/sf_util.c b/drivers/mxc/security/sahara2/sf_util.c
new file mode 100644
index 000000000000..12cc0161f4d4
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sf_util.c
@@ -0,0 +1,1390 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+* @file sf_util.c
+*
+* @brief Security Functions component API - Utility functions
+*
+* These are the 'Sahara api' functions which are used by the higher-level
+* FSL SHW API to build and then execute descriptor chains.
+*/
+
+
+#include "sf_util.h"
+#include <adaptor.h>
+
+#ifdef DIAG_SECURITY_FUNC
+#include <diagnostic.h>
+#endif /*DIAG_SECURITY_FUNC*/
+
+
+#ifdef __KERNEL__
+EXPORT_SYMBOL(sah_Append_Desc);
+EXPORT_SYMBOL(sah_Append_Link);
+EXPORT_SYMBOL(sah_Create_Link);
+EXPORT_SYMBOL(sah_Create_Key_Link);
+EXPORT_SYMBOL(sah_Destroy_Link);
+EXPORT_SYMBOL(sah_Descriptor_Chain_Execute);
+EXPORT_SYMBOL(sah_insert_mdha_algorithm);
+EXPORT_SYMBOL(sah_insert_skha_algorithm);
+EXPORT_SYMBOL(sah_insert_skha_mode);
+EXPORT_SYMBOL(sah_insert_skha_modulus);
+EXPORT_SYMBOL(sah_Descriptor_Chain_Destroy);
+EXPORT_SYMBOL(sah_add_two_in_desc);
+EXPORT_SYMBOL(sah_add_in_key_desc);
+EXPORT_SYMBOL(sah_add_two_out_desc);
+EXPORT_SYMBOL(sah_add_in_out_desc);
+EXPORT_SYMBOL(sah_add_key_out_desc);
+#endif
+
+#ifdef DEBUG_REWORK
+#ifndef __KERNEL__
+#include <stdio.h>
+#define os_printk printf
+#endif
+#endif
+
+/**
+ * Convert fsl_shw_hash_alg_t to mdha mode bits.
+ *
+ * Index must be maintained in order of fsl_shw_hash_alg_t enumeration!!!
+ */
+const uint32_t sah_insert_mdha_algorithm[] =
+{
+ [FSL_HASH_ALG_MD5] = sah_insert_mdha_algorithm_md5,
+ [FSL_HASH_ALG_SHA1] = sah_insert_mdha_algorithm_sha1,
+ [FSL_HASH_ALG_SHA224] = sah_insert_mdha_algorithm_sha224,
+ [FSL_HASH_ALG_SHA256] = sah_insert_mdha_algorithm_sha256,
+};
+
+/**
+ * Header bits for Algorithm field of SKHA header
+ *
+ * Index value must be kept in sync with fsl_shw_key_alg_t
+ */
+const uint32_t sah_insert_skha_algorithm[] =
+{
+ [FSL_KEY_ALG_HMAC] = 0x00000040,
+ [FSL_KEY_ALG_AES] = sah_insert_skha_algorithm_aes,
+ [FSL_KEY_ALG_DES] = sah_insert_skha_algorithm_des,
+ [FSL_KEY_ALG_TDES] = sah_insert_skha_algorithm_tdes,
+ [FSL_KEY_ALG_ARC4] = sah_insert_skha_algorithm_arc4,
+};
+
+
+/**
+ * Header bits for MODE field of SKHA header
+ *
+ * Index value must be kept in sync with fsl_shw_sym_mod_t
+ */
+const uint32_t sah_insert_skha_mode[] =
+{
+ [FSL_SYM_MODE_STREAM] = sah_insert_skha_mode_ecb,
+ [FSL_SYM_MODE_ECB] = sah_insert_skha_mode_ecb,
+ [FSL_SYM_MODE_CBC] = sah_insert_skha_mode_cbc,
+ [FSL_SYM_MODE_CTR] = sah_insert_skha_mode_ctr,
+};
+
+
+/**
+ * Header bits to set CTR modulus size. These have parity
+ * included to allow XOR insertion of values.
+ *
+ * @note Must be kept in sync with fsl_shw_ctr_mod_t
+ */
+const uint32_t sah_insert_skha_modulus[] =
+{
+ [FSL_CTR_MOD_8] = 0x00000000, /**< 2**8 */
+ [FSL_CTR_MOD_16] = 0x80000200, /**< 2**16 */
+ [FSL_CTR_MOD_24] = 0x80000400, /**< 2**24 */
+ [FSL_CTR_MOD_32] = 0x00000600, /**< 2**32 */
+ [FSL_CTR_MOD_40] = 0x80000800, /**< 2**40 */
+ [FSL_CTR_MOD_48] = 0x00000a00, /**< 2**48 */
+ [FSL_CTR_MOD_56] = 0x00000c00, /**< 2**56 */
+ [FSL_CTR_MOD_64] = 0x80000e00, /**< 2**64 */
+ [FSL_CTR_MOD_72] = 0x80001000, /**< 2**72 */
+ [FSL_CTR_MOD_80] = 0x00001200, /**< 2**80 */
+ [FSL_CTR_MOD_88] = 0x00001400, /**< 2**88 */
+ [FSL_CTR_MOD_96] = 0x80001600, /**< 2**96 */
+ [FSL_CTR_MOD_104] = 0x00001800, /**< 2**104 */
+ [FSL_CTR_MOD_112] = 0x80001a00, /**< 2**112 */
+ [FSL_CTR_MOD_120] = 0x80001c00, /**< 2**120 */
+ [FSL_CTR_MOD_128] = 0x00001e00 /**< 2**128 */
+};
+
+
+/******************************************************************************
+* Internal function declarations
+******************************************************************************/
+static fsl_shw_return_t sah_Create_Desc(
+ const sah_Mem_Util *mu,
+ sah_Desc ** desc,
+ int head,
+ uint32_t header,
+ sah_Link * link1,
+ sah_Link * link2);
+
+
+/**
+ * Create a descriptor chain using the the header and links passed in as
+ * parameters. The newly created descriptor will be added to the end of
+ * the descriptor chain passed.
+ *
+ * If @a desc_head points to a NULL value, then a sah_Head_Desc will be created
+ * as the first descriptor. Otherwise a sah_Desc will be created and appended.
+ *
+ * @pre
+ *
+ * - None
+ *
+ * @post
+ *
+ * - A descriptor has been created from the header, link1 and link2.
+ *
+ * - The newly created descriptor has been appended to the end of
+ * desc_head, or its location stored into the location pointed to by
+ * @a desc_head.
+ *
+ * - On allocation failure, @a link1 and @a link2 will be destroyed., and
+ * @a desc_head will be untouched.
+ *
+ * @brief Create and append descriptor chain, inserting header and links
+ * pointing to link1 and link2
+ *
+ * @param mu Memory functions
+ * @param header Value of descriptor header to be added
+ * @param desc_head Pointer to head of descriptor chain to append new desc
+ * @param link1 Pointer to sah_Link 1 (or NULL)
+ * @param link2 Pointer to sah_Link 2 (or NULL)
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_Append_Desc(
+ const sah_Mem_Util *mu,
+ sah_Head_Desc **desc_head,
+ const uint32_t header,
+ sah_Link *link1,
+ sah_Link *link2)
+{
+ fsl_shw_return_t status;
+ sah_Desc *desc;
+ sah_Desc *desc_ptr;
+
+
+ status = sah_Create_Desc(mu, (sah_Desc**)&desc, (*desc_head == NULL),
+ header, link1, link2);
+ /* append newly created descriptor to end of current chain */
+ if (status == FSL_RETURN_OK_S) {
+ if (*desc_head == NULL) {
+ (*desc_head) = (sah_Head_Desc*)desc;
+ (*desc_head)->out1_ptr = NULL;
+ (*desc_head)->out2_ptr = NULL;
+
+ } else {
+ desc_ptr = (sah_Desc*)*desc_head;
+ while (desc_ptr->next != NULL) {
+ desc_ptr = desc_ptr->next;
+ }
+ desc_ptr->next = desc;
+ }
+ }
+
+ return status;
+}
+
+
+/**
+ * Releases the memory allocated by the Security Function library for
+ * descriptors, links and any internally allocated memory referenced in the
+ * given chain. Note that memory allocated by user applications is not
+ * released.
+ *
+ * @post The @a desc_head pointer will be set to NULL to prevent further use.
+ *
+ * @brief Destroy a descriptor chain and free memory of associated links
+ *
+ * @param mu Memory functions
+ * @param desc_head Pointer to head of descriptor chain to be freed
+ *
+ * @return none
+ */
+void sah_Descriptor_Chain_Destroy (
+ const sah_Mem_Util *mu,
+ sah_Head_Desc **desc_head)
+{
+ sah_Desc *desc_ptr = &(*desc_head)->desc;
+ sah_Head_Desc *desc_head_ptr = (sah_Head_Desc *)desc_ptr;
+
+ while (desc_ptr != NULL) {
+ register sah_Desc *next_desc_ptr;
+
+ if (desc_ptr->ptr1 != NULL) {
+ sah_Destroy_Link(mu, desc_ptr->ptr1);
+ }
+ if (desc_ptr->ptr2 != NULL) {
+ sah_Destroy_Link(mu, desc_ptr->ptr2);
+ }
+
+ next_desc_ptr = desc_ptr->next;
+
+ /* Be sure to free head descriptor as such */
+ if (desc_ptr == (sah_Desc*)desc_head_ptr) {
+ mu->mu_free_head_desc(mu->mu_ref, desc_head_ptr);
+ } else {
+ mu->mu_free_desc(mu->mu_ref, desc_ptr);
+ }
+
+ desc_ptr = next_desc_ptr;
+ }
+
+ *desc_head = NULL;
+}
+
+
+#ifndef NO_INPUT_WORKAROUND
+/**
+ * Reworks the link chain
+ *
+ * @brief Reworks the link chain
+ *
+ * @param mu Memory functions
+ * @param link Pointer to head of link chain to be reworked
+ *
+ * @return none
+ */
+static fsl_shw_return_t sah_rework_link_chain(
+ const sah_Mem_Util *mu,
+ sah_Link* link)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ int found_potential_problem = 0;
+ uint32_t total_data = 0;
+#ifdef DEBUG_REWORK
+ sah_Link* first_link = link;
+#endif
+
+ if ((link->flags & SAH_OUTPUT_LINK)) {
+ return status;
+ }
+
+ while (link != NULL) {
+ total_data += link->len;
+
+ /* Only non-key Input Links are affected by the DMA flush-to-FIFO
+ * problem */
+
+ /* If have seen problem and at end of chain... */
+ if (found_potential_problem && (link->next == NULL) &&
+ (total_data > 16)) {
+ /* insert new 1-byte link */
+ sah_Link* new_tail_link = mu->mu_alloc_link(mu->mu_ref);
+ if (new_tail_link == NULL) {
+ status = FSL_RETURN_NO_RESOURCE_S;
+ } else {
+#ifdef DEBUG_REWORK
+ sah_Link* dump_link = first_link;
+ while (dump_link != NULL) {
+ uint32_t i;
+ unsigned bytes_to_dump = (dump_link->len > 32) ?
+ 32 : dump_link->len;
+ os_printk("(rework)Link %p: %p/%u/%p\n", dump_link,
+ dump_link->data, dump_link->len,
+ dump_link->next);
+ if (!(dump_link->flags & SAH_STORED_KEY_INFO)) {
+ os_printk("(rework)Data %p: ", dump_link->data);
+ for (i = 0; i < bytes_to_dump; i++) {
+ os_printk("%02X ", dump_link->data[i]);
+ }
+ os_printk("\n");
+ }
+ else {
+ os_printk("rework)Data %p: Red key data\n", dump_link);
+ }
+ dump_link = dump_link->next;
+ }
+#endif
+ link->len--;
+ link->next = new_tail_link;
+ new_tail_link->len = 1;
+ new_tail_link->data = link->data+link->len;
+ new_tail_link->flags = link->flags & ~(SAH_OWNS_LINK_DATA);
+ new_tail_link->next = NULL;
+ link = new_tail_link;
+#ifdef DEBUG_REWORK
+ os_printk("(rework)New link chain:\n");
+ dump_link = first_link;
+ while (dump_link != NULL) {
+ uint32_t i;
+ unsigned bytes_to_dump = (dump_link->len > 32) ?
+ 32 : dump_link->len;
+
+ os_printk("Link %p: %p/%u/%p\n", dump_link,
+ dump_link->data, dump_link->len,
+ dump_link->next);
+ if (!(dump_link->flags & SAH_STORED_KEY_INFO)) {
+ os_printk("Data %p: ", dump_link->data);
+ for (i = 0; i < bytes_to_dump; i++) {
+ os_printk("%02X ", dump_link->data[i]);
+ }
+ os_printk("\n");
+ }
+ else {
+ os_printk("Data %p: Red key data\n", dump_link);
+ }
+ dump_link = dump_link->next;
+ }
+#endif
+ }
+ } else if ((link->len % 4) || ((uint32_t)link->data % 4)) {
+ found_potential_problem = 1;
+ }
+
+ link = link->next;
+ }
+
+ return status;
+}
+
+
+/**
+ * Rework links to avoid H/W bug
+ *
+ * @param head Beginning of descriptor chain
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static fsl_shw_return_t sah_rework_links(
+ const sah_Mem_Util *mu,
+ sah_Head_Desc *head)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Desc* desc = &head->desc;
+
+ while ((status == FSL_RETURN_OK_S) && (desc != NULL)) {
+ if (desc->header & SAH_HDR_LLO) {
+ status = FSL_RETURN_ERROR_S;
+ break;
+ }
+ if (desc->ptr1 != NULL) {
+ status = sah_rework_link_chain(mu, desc->ptr1);
+ }
+ if ((status == FSL_RETURN_OK_S) && (desc->ptr2 != NULL)) {
+ status = sah_rework_link_chain(mu, desc->ptr2);
+ }
+ desc = desc->next;
+ }
+
+ return status;
+}
+#endif /* NO_INPUT_WORKAROUND */
+
+
+/**
+ * Send a descriptor chain to the SAHARA driver for processing.
+ *
+ * Note that SAHARA will read the input data from and write the output data
+ * directly to the locations indicated during construction of the chain.
+ *
+ * @pre
+ *
+ * - None
+ *
+ * @post
+ *
+ * - @a head will have been executed on SAHARA
+ * - @a head Will be freed unless a SAVE flag is set
+ *
+ * @brief Execute a descriptor chain
+ *
+ * @param head Pointer to head of descriptor chain to be executed
+ * @param user_ctx The user context on which to execute the descriptor chain.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_Descriptor_Chain_Execute(
+ sah_Head_Desc *head,
+ fsl_shw_uco_t *user_ctx)
+{
+ fsl_shw_return_t status;
+
+ /* Check for null pointer or non-multiple-of-four value */
+ if ((head == NULL) || ((uint32_t)head & 0x3)) {
+ status = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+#ifndef NO_INPUT_WORKAROUND
+ status = sah_rework_links(user_ctx->mem_util, head);
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+#endif
+
+ /* complete the information in the descriptor chain head node */
+ head->user_ref = user_ctx->user_ref;
+ head->uco_flags = user_ctx->flags;
+ head->next = NULL; /* driver will use this to link chain heads */
+
+ status = adaptor_Exec_Descriptor_Chain(head, user_ctx);
+
+#ifdef DIAG_SECURITY_FUNC
+ if (status == FSL_RETURN_OK_S)
+ LOG_DIAG("after exec desc chain: status is ok\n");
+ else
+ LOG_DIAG("after exec desc chain: status is not ok\n");
+#endif
+
+ out:
+ return status;
+}
+
+
+/**
+ * Create Link
+ *
+ * @brief Allocate Memory for Link structure and populate using input
+ * parameters
+ *
+ * @post On allocation failure, @a p will be freed if #SAH_OWNS_LINK_DATA is
+ * p set in @a flags.
+
+ * @param mu Memory functions
+ * @param link Pointer to link to be created
+ * @param p Pointer to data to use in link
+ * @param length Length of buffer 'p' in bytes
+ * @param flags Indicates whether memory has been allocated by the calling
+ * function or the security function
+ *
+ * @return FSL_RETURN_OK_S or FSL_RETURN_NO_RESOURCE_S
+ */
+fsl_shw_return_t sah_Create_Link(
+ const sah_Mem_Util *mu,
+ sah_Link **link,
+ uint8_t *p,
+ const size_t length,
+ const sah_Link_Flags flags)
+{
+
+#ifdef DIAG_SECURITY_FUNC
+
+ char diag[50];
+#endif /*DIAG_SECURITY_FUNC_UGLY*/
+ fsl_shw_return_t status = FSL_RETURN_NO_RESOURCE_S;
+
+
+ *link = mu->mu_alloc_link(mu->mu_ref);
+
+ /* populate link if memory allocation successful */
+ if (*link != NULL) {
+ (*link)->len = length;
+ (*link)->data = p;
+ (*link)->next = NULL;
+ (*link)->flags = flags;
+ status = FSL_RETURN_OK_S;
+
+#ifdef DIAG_SECURITY_FUNC
+
+ LOG_DIAG("Created Link");
+ LOG_DIAG("------------");
+ sprintf(diag," address = 0x%x", (int) *link);
+ LOG_DIAG(diag);
+ sprintf(diag," link->len = %d",(*link)->len);
+ LOG_DIAG(diag);
+ sprintf(diag," link->data = 0x%x",(int) (*link)->data);
+ LOG_DIAG(diag);
+ sprintf(diag," link->flags = 0x%x",(*link)->flags);
+ LOG_DIAG(diag);
+ LOG_DIAG(" link->next = NULL");
+#endif /*DIAG_SECURITY_FUNC_UGLY*/
+
+ } else {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Allocation of memory for sah_Link failed!\n");
+#endif /*DIAG_SECURITY_FUNC*/
+
+ /* Free memory previously allocated by the security function layer for
+ link data. Note that the memory being pointed to will be zeroed before
+ being freed, for security reasons. */
+ if (flags & SAH_OWNS_LINK_DATA) {
+ mu->mu_memset(mu->mu_ref, p, 0x00, length);
+ mu->mu_free(mu->mu_ref, p);
+ }
+ }
+
+ return status;
+}
+
+
+/**
+ * Create Key Link
+ *
+ * @brief Allocate Memory for Link structure and populate using key info
+ * object
+ *
+ * @param mu Memory functions
+ * @param link Pointer to store address of link to be created
+ * @param key_info Pointer to Key Info object to be referenced
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_Create_Key_Link(
+ const sah_Mem_Util *mu,
+ sah_Link **link,
+ fsl_shw_sko_t *key_info)
+{
+#ifdef DIAG_SECURITY_FUNC_UGLY
+ char diag[50];
+#endif /*DIAG_SECURITY_FUNC_UGLY*/
+ fsl_shw_return_t status = FSL_RETURN_NO_RESOURCE_S;
+ sah_Link_Flags flags = 0;
+
+
+ *link = mu->mu_alloc_link(mu->mu_ref);
+
+ /* populate link if memory allocation successful */
+ if (*link != NULL) {
+ (*link)->len = key_info->key_length;
+
+ if (key_info->flags & FSL_SKO_KEY_PRESENT) {
+ (*link)->data = key_info->key;
+ status = FSL_RETURN_OK_S;
+ } else {
+ if (key_info->flags & FSL_SKO_KEY_ESTABLISHED) {
+
+ if (key_info->keystore == NULL) {
+ /* System Keystore */
+ (*link)->slot = key_info->handle;
+ (*link)->ownerid = key_info->userid;
+ (*link)->data = 0;
+ flags |= SAH_STORED_KEY_INFO;
+ status = FSL_RETURN_OK_S;
+ } else {
+#ifdef FSL_HAVE_SCC2
+ /* User Keystore */
+ fsl_shw_kso_t *keystore = key_info->keystore;
+ /* Note: the key data is stored here, but the address has to
+ * be converted to a partition and offset in the kernel.
+ * This will be calculated in kernel space, based on the
+ * list of partitions held by the users context.
+ */
+ (*link)->data =
+ keystore->slot_get_address(keystore->user_data,
+ key_info->handle);
+
+ flags |= SAH_IN_USER_KEYSTORE;
+ status = FSL_RETURN_OK_S;
+#else
+ /* User keystores only supported in SCC2 */
+ status = FSL_RETURN_BAD_FLAG_S;
+#endif /* FSL_HAVE_SCC2 */
+
+ }
+ } else {
+ /* the flag is bad. Should never get here */
+ status = FSL_RETURN_BAD_FLAG_S;
+ }
+ }
+
+ (*link)->next = NULL;
+ (*link)->flags = flags;
+
+#ifdef DIAG_SECURITY_FUNC_UGLY
+ if (status == FSL_RETURN_OK_S) {
+ LOG_DIAG("Created Link");
+ LOG_DIAG("------------");
+ sprintf(diag," address = 0x%x", (int) *link);
+ LOG_DIAG(diag);
+ sprintf(diag," link->len = %d", (*link)->len);
+ LOG_DIAG(diag);
+ if (key_info->flags & FSL_SKO_KEY_ESTABLISHED) {
+ sprintf(diag," link->slot = 0x%x", (*link)->slot);
+ LOG_DIAG(diag);
+ } else {
+ sprintf(diag," link->data = 0x%x", (int)(*link)->data);
+ LOG_DIAG(diag);
+ }
+ sprintf(diag," link->flags = 0x%x", (*link)->flags);
+ LOG_DIAG(diag);
+ LOG_DIAG(" link->next = NULL");
+ }
+#endif /*DIAG_SECURITY_FUNC_UGLY*/
+
+ if (status == FSL_RETURN_BAD_FLAG_S) {
+ mu->mu_free_link(mu->mu_ref, *link);
+ *link = NULL;
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Creation of sah_Key_Link failed due to bad key flag!\n");
+#endif /*DIAG_SECURITY_FUNC*/
+ }
+
+ } else {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Allocation of memory for sah_Key_Link failed!\n");
+#endif /*DIAG_SECURITY_FUNC*/
+ }
+
+ return status;
+}
+
+
+/**
+ * Append Link
+ *
+ * @brief Allocate Memory for Link structure and append it to the end of
+ * the link chain.
+ *
+ * @post On allocation failure, @a p will be freed if #SAH_OWNS_LINK_DATA is
+ * p set in @a flags.
+ *
+ * @param mu Memory functions
+ * @param link_head Pointer to (head of) existing link chain
+ * @param p Pointer to data to use in link
+ * @param length Length of buffer 'p' in bytes
+ * @param flags Indicates whether memory has been allocated by the calling
+ * function or the security function
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_Append_Link(
+ const sah_Mem_Util *mu,
+ sah_Link *link_head,
+ uint8_t *p,
+ const size_t length,
+ const sah_Link_Flags flags)
+{
+ sah_Link* new_link;
+ fsl_shw_return_t status;
+
+
+ status = sah_Create_Link(mu, &new_link, p, length, flags);
+
+ if (status == FSL_RETURN_OK_S) {
+ /* chase for the tail */
+ while (link_head->next != NULL) {
+ link_head = link_head->next;
+ }
+
+ /* and add new tail */
+ link_head->next = new_link;
+ }
+
+ return status;
+}
+
+
+/**
+ * Create and populate a single descriptor
+ *
+ * The pointer and length fields will be be set based on the chains passed in
+ * as @a link1 and @a link2.
+ *
+ * @param mu Memory utility suite
+ * @param desc Location to store pointer of new descriptor
+ * @param head_desc Non-zero if this will be first in chain; zero otherwise
+ * @param header The Sahara header value to store in the descriptor
+ * @param link1 A value (or NULL) for the first ptr
+ * @param link2 A value (or NULL) for the second ptr
+ *
+ * @post If allocation succeeded, the @a link1 and @link2 will be linked into
+ * the descriptor. If allocation failed, those link structues will be
+ * freed, and the @a desc will be unchanged.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static fsl_shw_return_t sah_Create_Desc(
+ const sah_Mem_Util *mu,
+ sah_Desc ** desc,
+ int head_desc,
+ uint32_t header,
+ sah_Link * link1,
+ sah_Link * link2)
+{
+ fsl_shw_return_t status = FSL_RETURN_NO_RESOURCE_S;
+#ifdef DIAG_SECURITY_FUNC_UGLY
+ char diag[50];
+#endif /*DIAG_SECURITY_FUNC_UGLY*/
+
+
+ if (head_desc != 0) {
+ *desc = (sah_Desc *)mu->mu_alloc_head_desc(mu->mu_ref);
+ } else {
+ *desc = mu->mu_alloc_desc(mu->mu_ref);
+ }
+
+ /* populate descriptor if memory allocation successful */
+ if ((*desc) != NULL) {
+ sah_Link* temp_link;
+
+ status = FSL_RETURN_OK_S;
+ (*desc)->header = header;
+
+ temp_link = (*desc)->ptr1 = link1;
+ (*desc)->len1 = 0;
+ while (temp_link != NULL) {
+ (*desc)->len1 += temp_link->len;
+ temp_link = temp_link->next;
+ }
+
+ temp_link = (*desc)->ptr2 = link2;
+ (*desc)->len2 = 0;
+ while (temp_link != NULL) {
+ (*desc)->len2 += temp_link->len;
+ temp_link = temp_link->next;
+ }
+
+ (*desc)->next = NULL;
+
+#ifdef DIAG_SECURITY_FUNC_UGLY
+ LOG_DIAG("Created Desc");
+ LOG_DIAG("------------");
+ sprintf(diag," address = 0x%x",(int) *desc);
+ LOG_DIAG(diag);
+ sprintf(diag," desc->header = 0x%x",(*desc)->header);
+ LOG_DIAG(diag);
+ sprintf(diag," desc->len1 = %d",(*desc)->len1);
+ LOG_DIAG(diag);
+ sprintf(diag," desc->ptr1 = 0x%x",(int) ((*desc)->ptr1));
+ LOG_DIAG(diag);
+ sprintf(diag," desc->len2 = %d",(*desc)->len2);
+ LOG_DIAG(diag);
+ sprintf(diag," desc->ptr2 = 0x%x",(int) ((*desc)->ptr2));
+ LOG_DIAG(diag);
+ sprintf(diag," desc->next = 0x%x",(int) ((*desc)->next));
+ LOG_DIAG(diag);
+#endif /*DIAG_SECURITY_FUNC_UGLY*/
+ } else {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Allocation of memory for sah_Desc failed!\n");
+#endif /*DIAG_SECURITY_FUNC*/
+
+ /* Destroy the links, otherwise the memory allocated by the Security
+ Function layer for the links (and possibly the data within the links)
+ will be lost */
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ }
+
+ return status;
+}
+
+
+/**
+ * Destroy a link (chain) and free memory
+ *
+ * @param mu memory utility suite
+ * @param link The Link to destroy
+ *
+ * @return none
+ */
+void sah_Destroy_Link(
+ const sah_Mem_Util *mu,
+ sah_Link * link)
+{
+
+ while (link != NULL) {
+ sah_Link * next_link = link->next;
+
+ if (link->flags & SAH_OWNS_LINK_DATA) {
+ /* zero data for security purposes */
+ mu->mu_memset(mu->mu_ref, link->data, 0x00, link->len);
+ mu->mu_free(mu->mu_ref, link->data);
+ }
+
+ link->data = NULL;
+ link->next = NULL;
+ mu->mu_free_link(mu->mu_ref, link);
+
+ link = next_link;
+ }
+}
+
+
+/**
+ * Add descriptor where both links are inputs.
+ *
+ * @param header The Sahara header value for the descriptor.
+ * @param in1 The first input buffer (or NULL)
+ * @param in1_length Size of @a in1
+ * @param[out] in2 The second input buffer (or NULL)
+ * @param in2_length Size of @a in2
+ * @param mu Memory functions
+ * @param[in,out] desc_chain Chain to start or append to
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_add_two_in_desc(uint32_t header,
+ const uint8_t* in1,
+ uint32_t in1_length,
+ const uint8_t* in2,
+ uint32_t in2_length,
+ const sah_Mem_Util* mu,
+ sah_Head_Desc** desc_chain)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Link* link1 = NULL;
+ sah_Link* link2 = NULL;
+
+ if (in1 != NULL) {
+ status = sah_Create_Link(mu, &link1,
+ (sah_Oct_Str) in1, in1_length, SAH_USES_LINK_DATA);
+ }
+
+ if ( (in2 != NULL) && (status == FSL_RETURN_OK_S) ) {
+ status = sah_Create_Link(mu, &link2,
+ (sah_Oct_Str) in2, in2_length,
+ SAH_USES_LINK_DATA);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ } else {
+ status = sah_Append_Desc(mu, desc_chain, header, link1, link2);
+ }
+
+ return status;
+}
+
+/*!
+ * Add descriptor where neither link needs sync
+ *
+ * @param header The Sahara header value for the descriptor.
+ * @param in1 The first input buffer (or NULL)
+ * @param in1_length Size of @a in1
+ * @param[out] in2 The second input buffer (or NULL)
+ * @param in2_length Size of @a in2
+ * @param mu Memory functions
+ * @param[in,out] desc_chain Chain to start or append to
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_add_two_d_desc(uint32_t header,
+ const uint8_t * in1,
+ uint32_t in1_length,
+ const uint8_t * in2,
+ uint32_t in2_length,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+
+ printk("Entering sah_add_two_d_desc \n");
+
+ if (in1 != NULL) {
+ status = sah_Create_Link(mu, &link1,
+ (sah_Oct_Str) in1, in1_length,
+ SAH_USES_LINK_DATA);
+ }
+
+ if ((in2 != NULL) && (status == FSL_RETURN_OK_S)) {
+ status = sah_Create_Link(mu, &link2,
+ (sah_Oct_Str) in2, in2_length,
+ SAH_USES_LINK_DATA);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ } else {
+ status = sah_Append_Desc(mu, desc_chain, header, link1, link2);
+ }
+
+ return status;
+} /* sah_add_two_d_desc() */
+
+/**
+ * Add descriptor where both links are inputs, the second one being a key.
+ *
+ * @param header The Sahara header value for the descriptor.
+ * @param in1 The first input buffer (or NULL)
+ * @param in1_length Size of @a in1
+ * @param[out] in2 The second input buffer (or NULL)
+ * @param in2_length Size of @a in2
+ * @param mu Memory functions
+ * @param[in,out] desc_chain Chain to start or append to
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_add_in_key_desc(uint32_t header,
+ const uint8_t* in1,
+ uint32_t in1_length,
+ fsl_shw_sko_t* key_info,
+ const sah_Mem_Util* mu,
+ sah_Head_Desc** desc_chain)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+
+ if (in1 != NULL) {
+ status = sah_Create_Link(mu, &link1,
+ (sah_Oct_Str) in1, in1_length,
+ SAH_USES_LINK_DATA);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ status = sah_Create_Key_Link(mu, &link2, key_info);
+
+
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ status = sah_Append_Desc(mu, desc_chain, header, link1, link2);
+
+out:
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ }
+
+ return status;
+}
+
+
+/**
+ * Create two links using keys allocated in the scc
+ *
+ * @param header The Sahara header value for the descriptor.
+ * @param in1 The first input buffer (or NULL)
+ * @param in1_length Size of @a in1
+ * @param[out] in2 The second input buffer (or NULL)
+ * @param in2_length Size of @a in2
+ * @param mu Memory functions
+ * @param[in,out] desc_chain Chain to start or append to
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_add_key_key_desc(uint32_t header,
+ fsl_shw_sko_t *key_info1,
+ fsl_shw_sko_t *key_info2,
+ const sah_Mem_Util *mu,
+ sah_Head_Desc **desc_chain)
+{
+ fsl_shw_return_t status;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+
+
+ status = sah_Create_Key_Link(mu, &link1, key_info1);
+
+ if (status == FSL_RETURN_OK_S) {
+ status = sah_Create_Key_Link(mu, &link2, key_info2);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ } else {
+ status = sah_Append_Desc(mu, desc_chain, header, link1, link2);
+ }
+
+ return status;
+}
+
+
+/**
+ * Add descriptor where first link is input, the second is a changing key
+ *
+ * @param header The Sahara header value for the descriptor.
+ * @param in1 The first input buffer (or NULL)
+ * @param in1_length Size of @a in1
+ * @param[out] in2 The key for output
+ * @param mu Memory functions
+ * @param[in,out] desc_chain Chain to start or append to
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_add_in_keyout_desc(uint32_t header,
+ const uint8_t* in1,
+ uint32_t in1_length,
+ fsl_shw_sko_t* key_info,
+ const sah_Mem_Util* mu,
+ sah_Head_Desc** desc_chain)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+
+ if (in1 != NULL) {
+ status = sah_Create_Link(mu, &link1,
+ (sah_Oct_Str) in1, in1_length,
+ SAH_USES_LINK_DATA);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ status = sah_Create_Key_Link(mu, &link2, key_info);
+
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+link2->flags |= SAH_OUTPUT_LINK; /* mark key for output */
+status = sah_Append_Desc(mu, desc_chain, header, link1, link2);
+
+out:
+
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ }
+
+ return status;
+}
+
+/**
+ * Add descriptor where both links are outputs.
+ *
+ * @param header The Sahara header value for the descriptor.
+ * @param out1 The first output buffer (or NULL)
+ * @param out1_length Size of @a out1
+ * @param[out] out2 The second output buffer (or NULL)
+ * @param out2_length Size of @a out2
+ * @param mu Memory functions
+ * @param[in,out] desc_chain Chain to start or append to
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_add_two_out_desc(uint32_t header,
+ uint8_t* out1,
+ uint32_t out1_length,
+ uint8_t* out2,
+ uint32_t out2_length,
+ const sah_Mem_Util* mu,
+ sah_Head_Desc** desc_chain)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+
+
+ if (out1 != NULL) {
+ status = sah_Create_Link(mu, &link1,
+ (sah_Oct_Str) out1, out1_length,
+ SAH_OUTPUT_LINK | SAH_USES_LINK_DATA);
+ }
+
+ if ( (out2 != NULL) && (status == FSL_RETURN_OK_S) ) {
+ status = sah_Create_Link(mu, &link2,
+ (sah_Oct_Str) out2, out2_length,
+ SAH_OUTPUT_LINK |
+ SAH_USES_LINK_DATA);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ } else {
+ status = sah_Append_Desc(mu, desc_chain, header, link1, link2);
+ }
+
+ return status;
+}
+
+
+/**
+ * Add descriptor where first link is output, second is output
+ *
+ * @param header The Sahara header value for the descriptor.
+ * @param out1 The first output buffer (or NULL)
+ * @param out1_length Size of @a out1
+ * @param[out] in2 The input buffer (or NULL)
+ * @param in2_length Size of @a in2
+ * @param mu Memory functions
+ * @param[in,out] desc_chain Chain to start or append to
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_add_out_in_desc(uint32_t header,
+ uint8_t* out1,
+ uint32_t out1_length,
+ const uint8_t* in2,
+ uint32_t in2_length,
+ const sah_Mem_Util* mu,
+ sah_Head_Desc** desc_chain)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+
+
+ if (out1 != NULL) {
+ status = sah_Create_Link(mu, &link1,
+ (sah_Oct_Str) out1, out1_length,
+ SAH_OUTPUT_LINK |
+ SAH_USES_LINK_DATA);
+ }
+
+ if ( (in2 != NULL) && (status == FSL_RETURN_OK_S) ) {
+ status = sah_Create_Link(mu, &link2,
+ (sah_Oct_Str) in2, in2_length,
+ SAH_USES_LINK_DATA);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ } else {
+ status = sah_Append_Desc(mu, desc_chain, header, link1, link2);
+ }
+
+ return status;
+}
+
+
+/**
+ * Add descriptor where link1 is input buffer, link2 is output buffer.
+ *
+ * @param header The Sahara header value for the descriptor.
+ * @param in The input buffer
+ * @param in_length Size of the input buffer
+ * @param[out] out The output buffer
+ * @param out_length Size of the output buffer
+ * @param mu Memory functions
+ * @param[in,out] desc_chain Chain to start or append to
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_add_in_out_desc(uint32_t header,
+ const uint8_t* in, uint32_t in_length,
+ uint8_t* out, uint32_t out_length,
+ const sah_Mem_Util* mu,
+ sah_Head_Desc** desc_chain)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+
+ if (in != NULL) {
+ status = sah_Create_Link(mu, &link1,
+ (sah_Oct_Str) in, in_length,
+ SAH_USES_LINK_DATA);
+ }
+
+ if ((status == FSL_RETURN_OK_S) && (out != NULL)) {
+ status = sah_Create_Link(mu, &link2,
+ (sah_Oct_Str) out, out_length,
+ SAH_OUTPUT_LINK |
+ SAH_USES_LINK_DATA);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ } else {
+ status = sah_Append_Desc(mu, desc_chain, header, link1, link2);
+ }
+
+ return status;
+}
+
+
+/**
+ * Add descriptor where link1 is a key, link2 is output buffer.
+ *
+ * @param header The Sahara header value for the descriptor.
+ * @param key_info Key information
+ * @param[out] out The output buffer
+ * @param out_length Size of the output buffer
+ * @param mu Memory functions
+ * @param[in,out] desc_chain Chain to start or append to
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_add_key_out_desc(uint32_t header,
+ const fsl_shw_sko_t *key_info,
+ uint8_t* out, uint32_t out_length,
+ const sah_Mem_Util *mu,
+ sah_Head_Desc **desc_chain)
+{
+ fsl_shw_return_t status;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+
+ status = sah_Create_Key_Link(mu, &link1, (fsl_shw_sko_t *) key_info);
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+
+ if (out != NULL) {
+ status = sah_Create_Link(mu, &link2,
+ (sah_Oct_Str) out, out_length,
+ SAH_OUTPUT_LINK |
+ SAH_USES_LINK_DATA);
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ }
+status = sah_Append_Desc(mu, desc_chain, header, link1, link2);
+
+out:
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ }
+
+ return status;
+}
+
+
+/**
+ * Sanity checks the user context object fields to ensure that they make some
+ * sense before passing the uco as a parameter
+ *
+ * @brief Verify the user context object
+ *
+ * @param uco user context object
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_validate_uco(fsl_shw_uco_t *uco)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+
+
+ /* check if file is opened */
+ if (uco->sahara_openfd < 0) {
+ status = FSL_RETURN_NO_RESOURCE_S;
+ } else {
+ /* check flag combination: the only invalid setting of the
+ * blocking and callback flags is blocking with callback. So check
+ * for that
+ */
+ if ((uco->flags & (FSL_UCO_BLOCKING_MODE | FSL_UCO_CALLBACK_MODE)) ==
+ (FSL_UCO_BLOCKING_MODE | FSL_UCO_CALLBACK_MODE)) {
+ status = FSL_RETURN_BAD_FLAG_S;
+ } else {
+ /* check that memory utilities have been attached */
+ if (uco->mem_util == NULL) {
+ status = FSL_RETURN_MEMORY_ERROR_S;
+ } else {
+ /* must have pool of at least 1, even for blocking mode */
+ if (uco->pool_size == 0) {
+ status = FSL_RETURN_ERROR_S;
+ } else {
+ /* if callback flag is set, it better have a callback
+ * routine */
+ if (uco->flags & FSL_UCO_CALLBACK_MODE) {
+ if (uco->callback == NULL) {
+ status = FSL_RETURN_INTERNAL_ERROR_S;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return status;
+}
+
+
+/**
+ * Perform any post-processing on non-blocking results.
+ *
+ * For instance, free descriptor chains, compare authentication codes, ...
+ *
+ * @param user_ctx User context object
+ * @param result_info A set of results
+ */
+void sah_Postprocess_Results(fsl_shw_uco_t* user_ctx, sah_results* result_info)
+{
+ unsigned i;
+
+ /* for each result returned */
+ for (i = 0; i < *result_info->actual; i++) {
+ sah_Head_Desc* desc = result_info->results[i].user_desc;
+ uint8_t* out1 = desc->out1_ptr;
+ uint8_t* out2 = desc->out2_ptr;
+ uint32_t len = desc->out_len;
+
+ /*
+ * For now, tne only post-processing besides freeing the
+ * chain is the need to check the auth code for fsl_shw_auth_decrypt().
+ *
+ * If other uses are required in the future, this code will probably
+ * need a flag in the sah_Head_Desc (or a function pointer!) to
+ * determine what needs to be done.
+ */
+ if ((out1 != NULL) && (out2 != NULL)) {
+ unsigned j;
+ for (j = 0; j < len; j++) {
+ if (out1[j] != out2[j]) {
+ /* Problem detected. Change result. */
+ result_info->results[i].code = FSL_RETURN_AUTH_FAILED_S;
+ break;
+ }
+ }
+ /* free allocated 'calced_auth' */
+ user_ctx->mem_util->
+ mu_free(user_ctx->mem_util->mu_ref, out1);
+ }
+
+ /* Free the API-created chain, unless tagged as not-from-API */
+ if (! (desc->uco_flags & FSL_UCO_SAVE_DESC_CHAIN)) {
+ sah_Descriptor_Chain_Destroy(user_ctx->mem_util, &desc);
+ }
+ }
+}
+
+
+/* End of sf_util.c */
diff --git a/drivers/mxc/security/scc2_driver.c b/drivers/mxc/security/scc2_driver.c
new file mode 100644
index 000000000000..e0cbb28a0ad0
--- /dev/null
+++ b/drivers/mxc/security/scc2_driver.c
@@ -0,0 +1,2401 @@
+/*
+ * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*! @file scc2_driver.c
+ *
+ * This is the driver code for the Security Controller version 2 (SCC2). It's
+ * interaction with the Linux kernel is from calls to #scc_init() when the
+ * driver is loaded, and #scc_cleanup() should the driver be unloaded. The
+ * driver uses locking and (task-sleep/task-wakeup) functions from the kernel.
+ * It also registers itself to handle the interrupt line(s) from the SCC. New
+ * to this version of the driver is an interface providing access to the secure
+ * partitions. This is in turn exposed to the API user through the
+ * fsl_shw_smalloc() series of functions. Other drivers in the kernel may use
+ * the remaining API functions to get at the services of the SCC. The main
+ * service provided is the Secure Memory, which allows encoding and decoding of
+ * secrets with a per-chip secret key.
+ *
+ * The SCC is single-threaded, and so is this module. When the scc_crypt()
+ * routine is called, it will lock out other accesses to the function. If
+ * another task is already in the module, the subsequent caller will spin on a
+ * lock waiting for the other access to finish.
+ *
+ * Note that long crypto operations could cause a task to spin for a while,
+ * preventing other kernel work (other than interrupt processing) to get done.
+ *
+ * The external (kernel module) interface is through the following functions:
+ * @li scc_get_configuration() @li scc_crypt() @li scc_zeroize_memories() @li
+ * scc_monitor_security_failure() @li scc_stop_monitoring_security_failure()
+ * @li scc_set_sw_alarm() @li scc_read_register() @li scc_write_register() @li
+ * scc_allocate_partition() @li scc_initialize_partition @li
+ * scc_release_partition() @li scc_diminish_permissions @li
+ * scc_encrypt_region() @li scc_decrypt_region() @li scc_virt_to_phys
+ *
+ * All other functions are internal to the driver.
+ */
+
+#include "sahara2/include/portable_os.h"
+#include "scc2_internals.h"
+#include <linux/delay.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
+
+#include <linux/device.h>
+#include <mach/clock.h>
+#include <linux/device.h>
+
+#else
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#endif
+
+#include <linux/dmapool.h>
+
+/**
+ * This is the set of errors which signal that access to the SCM RAM has
+ * failed or will fail.
+ */
+#define SCM_ACCESS_ERRORS \
+ (SCM_ERRSTAT_ILM | SCM_ERRSTAT_SUP | SCM_ERRSTAT_ERC_MASK)
+
+/******************************************************************************
+ *
+ * Global / Static Variables
+ *
+ *****************************************************************************/
+
+#ifdef SCC_REGISTER_DEBUG
+
+#define REG_PRINT_BUFFER_SIZE 200
+
+static char reg_print_buffer[REG_PRINT_BUFFER_SIZE];
+
+typedef char *(*reg_print_routine_t) (uint32_t value, char *print_buffer,
+ int buf_size);
+
+#endif
+
+/**
+ * This is type void* so that a) it cannot directly be dereferenced,
+ * and b) pointer arithmetic on it will function in a 'normal way' for
+ * the offsets in scc_defines.h
+ *
+ * scc_base is the location in the iomap where the SCC's registers
+ * (and memory) start.
+ *
+ * The referenced data is declared volatile so that the compiler will
+ * not make any assumptions about the value of registers in the SCC,
+ * and thus will always reload the register into CPU memory before
+ * using it (i.e. wherever it is referenced in the driver).
+ *
+ * This value should only be referenced by the #SCC_READ_REGISTER and
+ * #SCC_WRITE_REGISTER macros and their ilk. All dereferences must be
+ * 32 bits wide.
+ */
+static volatile void *scc_base;
+uint32_t scc_phys_base;
+
+/** Array to hold function pointers registered by
+ #scc_monitor_security_failure() and processed by
+ #scc_perform_callbacks() */
+static void (*scc_callbacks[SCC_CALLBACK_SIZE]) (void);
+/*SCC need IRAM's base address but use only the partitions allocated for it.*/
+uint32_t scm_ram_phys_base;
+
+void *scm_ram_base = NULL;
+
+/** Calculated once for quick reference to size of the unreserved space in
+ * RAM in SCM.
+ */
+uint32_t scm_memory_size_bytes;
+
+/** Structure returned by #scc_get_configuration() */
+static scc_config_t scc_configuration = {
+ .driver_major_version = SCC_DRIVER_MAJOR_VERSION,
+ .driver_minor_version = SCC_DRIVER_MINOR_VERSION_2,
+ .scm_version = -1,
+ .smn_version = -1,
+ .block_size_bytes = -1,
+ .partition_size_bytes = -1,
+ .partition_count = -1,
+};
+
+/** Internal flag to know whether SCC is in Failed state (and thus many
+ * registers are unavailable). Once it goes failed, it never leaves it. */
+static volatile enum scc_status scc_availability = SCC_STATUS_INITIAL;
+
+/** Flag to say whether interrupt handler has been registered for
+ * SMN interrupt */
+static int smn_irq_set = 0;
+
+/** Flag to say whether interrupt handler has been registered for
+ * SCM interrupt */
+static int scm_irq_set = 0;
+
+/** This lock protects the #scc_callbacks list as well as the @c
+ * callbacks_performed flag in #scc_perform_callbacks. Since the data this
+ * protects may be read or written from either interrupt or base level, all
+ * operations should use the irqsave/irqrestore or similar to make sure that
+ * interrupts are inhibited when locking from base level.
+ */
+static os_lock_t scc_callbacks_lock = NULL;
+
+/**
+ * Ownership of this lock prevents conflicts on the crypto operation in the
+ * SCC.
+ */
+static os_lock_t scc_crypto_lock = NULL;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18))
+/** Pointer to SCC's clock information. Initialized during scc_init(). */
+static struct clk *scc_clk = NULL;
+#endif
+
+/** The lookup table for an 8-bit value. Calculated once
+ * by #scc_init_ccitt_crc().
+ */
+static uint16_t scc_crc_lookup_table[256];
+
+/******************************************************************************
+ *
+ * Function Implementations - Externally Accessible
+ *
+ *****************************************************************************/
+
+/**
+ * Allocate a partition of secure memory
+ *
+ * @param smid_value Value to use for the SMID register. Must be 0 for
+ * kernel mode access.
+ * @param[out] part_no (If successful) Assigned partition number.
+ * @param[out] part_base Kernel virtual address of the partition.
+ * @param[out] part_phys Physical address of the partition.
+ *
+ * @return
+ */
+scc_return_t scc_allocate_partition(uint32_t smid_value,
+ int *part_no,
+ void **part_base, uint32_t *part_phys)
+{
+ uint32_t i;
+ os_lock_context_t irq_flags = 0; /* for IRQ save/restore */
+ int local_part;
+ scc_return_t retval = SCC_RET_FAIL;
+ void *base_addr = NULL;
+ uint32_t reg_value;
+
+ local_part = -1;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+ if (scc_availability == SCC_STATUS_UNIMPLEMENTED) {
+ goto out;
+ }
+
+ /* ACQUIRE LOCK to prevent others from using crypto or acquiring a
+ * partition. Note that crypto operations could take a long time, so the
+ * calling process could potentially spin for some time.
+ */
+ os_lock_save_context(scc_crypto_lock, irq_flags);
+
+ do {
+ /* Find current state of partition ownership */
+ reg_value = SCC_READ_REGISTER(SCM_PART_OWNERS_REG);
+
+ /* Search for a free one */
+ for (i = 0; i < scc_configuration.partition_count; i++) {
+ if (((reg_value >> (SCM_POWN_SHIFT * i))
+ & SCM_POWN_MASK) == SCM_POWN_PART_FREE) {
+ break; /* found a free one */
+ }
+ }
+ if (i == local_part) {
+ /* found this one last time, and failed to allocated it */
+ pr_debug(KERN_ERR "Partition %d cannot be allocated\n",
+ i);
+ goto out;
+ }
+ if (i >= scc_configuration.partition_count) {
+ retval = SCC_RET_INSUFFICIENT_SPACE; /* all used up */
+ goto out;
+ }
+
+ pr_debug
+ ("SCC2: Attempting to allocate partition %i, owners:%08x\n",
+ i, SCC_READ_REGISTER(SCM_PART_OWNERS_REG));
+
+ local_part = i;
+ /* Store SMID to grab a partition */
+ SCC_WRITE_REGISTER(SCM_SMID0_REG +
+ SCM_SMID_WIDTH * (local_part), smid_value);
+ mdelay(2);
+
+ /* Now make sure it is ours... ? */
+ reg_value = SCC_READ_REGISTER(SCM_PART_OWNERS_REG);
+
+ if (((reg_value >> (SCM_POWN_SHIFT * (local_part)))
+ & SCM_POWN_MASK) != SCM_POWN_PART_OWNED) {
+ continue; /* try for another */
+ }
+ base_addr = scm_ram_base +
+ (local_part * scc_configuration.partition_size_bytes);
+ break;
+ } while (1);
+
+out:
+
+ /* Free the lock */
+ os_unlock_restore_context(scc_callbacks_lock, irq_flags);
+
+ /* If the base address was assigned, then a partition was successfully
+ * acquired.
+ */
+ if (base_addr != NULL) {
+ pr_debug("SCC2 Part owners: %08x, engaged: %08x\n",
+ reg_value, SCC_READ_REGISTER(SCM_PART_ENGAGED_REG));
+ pr_debug("SCC2 MAP for part %d: %08x\n",
+ local_part,
+ SCC_READ_REGISTER(SCM_ACC0_REG + 8 * local_part));
+
+ /* Copy the partition information to the data structures passed by the
+ * user.
+ */
+ *part_no = local_part;
+ *part_base = base_addr;
+ *part_phys = (uint32_t) scm_ram_phys_base
+ + (local_part * scc_configuration.partition_size_bytes);
+ retval = SCC_RET_OK;
+
+ pr_debug
+ ("SCC2 partition engaged. Kernel address: %p. Physical "
+ "address: %p, pfn: %08x\n", *part_base, (void *)*part_phys,
+ __phys_to_pfn(*part_phys));
+ }
+
+ return retval;
+} /* allocate_partition() */
+
+/**
+ * Release a partition of secure memory
+ *
+ * @param part_base Kernel virtual address of the partition to be released.
+ *
+ * @return SCC_RET_OK if successful.
+ */
+scc_return_t scc_release_partition(void *part_base)
+{
+ uint32_t partition_no;
+
+ if (part_base == NULL) {
+ return SCC_RET_FAIL;
+ }
+
+ /* Ensure that this is a proper partition location */
+ partition_no = SCM_PART_NUMBER((uint32_t) part_base);
+
+ pr_debug("SCC2: Attempting to release partition %i, owners:%08x\n",
+ partition_no, SCC_READ_REGISTER(SCM_PART_OWNERS_REG));
+
+ /* check that the partition is ours to de-establish */
+ if (!host_owns_partition(partition_no)) {
+ return SCC_RET_FAIL;
+ }
+
+ /* TODO: The state of the zeroize engine (SRS field in the Command Status
+ * Register) should be examined before issuing the zeroize command here.
+ * To make the driver thread-safe, a lock should be taken out before
+ * issuing the check and released after the zeroize command has been
+ * issued.
+ */
+
+ /* Zero the partition to release it */
+ scc_write_register(SCM_ZCMD_REG,
+ (partition_no << SCM_ZCMD_PART_SHIFT) |
+ (ZCMD_DEALLOC_PART << SCM_ZCMD_CCMD_SHIFT));
+ mdelay(2);
+
+ pr_debug("SCC2: done releasing partition %i, owners:%08x\n",
+ partition_no, SCC_READ_REGISTER(SCM_PART_OWNERS_REG));
+
+ /* Check that the de-assignment went correctly */
+ if (host_owns_partition(partition_no)) {
+ return SCC_RET_FAIL;
+ }
+
+ return SCC_RET_OK;
+}
+
+/**
+ * Diminish the permissions on a partition of secure memory
+ *
+ * @param part_base Kernel virtual address of the partition.
+ * @param permissions ORed values of the type SCM_PERM_* which will be used as
+ * initial partition permissions. SHW API users should use
+ * the FSL_PERM_* definitions instead.
+ *
+ * @return SCC_RET_OK if successful.
+ */
+scc_return_t scc_diminish_permissions(void *part_base, uint32_t permissions)
+{
+ uint32_t partition_no;
+ uint32_t permissions_requested;
+ permissions_requested = permissions;
+
+ /* ensure that this is a proper partition location */
+ partition_no = SCM_PART_NUMBER((uint32_t) part_base);
+
+ /* invert the permissions, masking out unused bits */
+ permissions = (~permissions) & SCM_PERM_MASK;
+
+ /* attempt to diminish the permissions */
+ scc_write_register(SCM_ACC0_REG + 8 * partition_no, permissions);
+ mdelay(2);
+
+ /* Reading it back puts it into the original form */
+ permissions = SCC_READ_REGISTER(SCM_ACC0_REG + 8 * partition_no);
+ if (permissions == permissions_requested) {
+ pr_debug("scc_partition_diminish_perms: successful\n");
+ pr_debug("scc_partition_diminish_perms: successful\n");
+ return SCC_RET_OK;
+ }
+ pr_debug("scc_partition_diminish_perms: not successful\n");
+
+ return SCC_RET_FAIL;
+}
+
+extern scc_partition_status_t scc_partition_status(void *part_base)
+{
+ uint32_t part_no;
+ uint32_t part_owner;
+
+ /* Determine the partition number from the address */
+ part_no = SCM_PART_NUMBER((uint32_t) part_base);
+
+ /* Check if the partition is implemented */
+ if (part_no >= scc_configuration.partition_count) {
+ return SCC_PART_S_UNUSABLE;
+ }
+
+ /* Determine the value of the partition owners register */
+ part_owner = (SCC_READ_REGISTER(SCM_PART_OWNERS_REG)
+ >> (part_no * SCM_POWN_SHIFT)) & SCM_POWN_MASK;
+
+ switch (part_owner) {
+ case SCM_POWN_PART_OTHER:
+ return SCC_PART_S_UNAVAILABLE;
+ break;
+ case SCM_POWN_PART_FREE:
+ return SCC_PART_S_AVAILABLE;
+ break;
+ case SCM_POWN_PART_OWNED:
+ /* could be allocated or engaged*/
+ if (partition_engaged(part_no)) {
+ return SCC_PART_S_ENGAGED;
+ } else {
+ return SCC_PART_S_ALLOCATED;
+ }
+ break;
+ case SCM_POWN_PART_UNUSABLE:
+ default:
+ return SCC_PART_S_UNUSABLE;
+ break;
+ }
+}
+
+/**
+ * Calculate the physical address from the kernel virtual address.
+ *
+ * @param address Kernel virtual address of data in an Secure Partition.
+ * @return Physical address of said data.
+ */
+uint32_t scc_virt_to_phys(void *address)
+{
+ return (uint32_t) address - (uint32_t) scm_ram_base
+ + (uint32_t) scm_ram_phys_base;
+}
+
+/**
+ * Engage partition of secure memory
+ *
+ * @param part_base (kernel) Virtual
+ * @param UMID NULL, or 16-byte UMID for partition security
+ * @param permissions ORed values from fsl_shw_permission_t which
+ * will be used as initial partiition permissions.
+ *
+ * @return SCC_RET_OK if successful.
+ */
+
+scc_return_t
+scc_engage_partition(void *part_base,
+ const uint8_t *UMID, uint32_t permissions)
+{
+ uint32_t partition_no;
+ uint8_t *UMID_base = part_base + 0x10;
+ uint32_t *MAP_base = part_base;
+ uint8_t i;
+
+ partition_no = SCM_PART_NUMBER((uint32_t) part_base);
+
+ if (!host_owns_partition(partition_no) ||
+ partition_engaged(partition_no) ||
+ !(SCC_READ_REGISTER(SCM_SMID0_REG + (partition_no * 8)) == 0)) {
+
+ return SCC_RET_FAIL;
+ }
+
+ if (UMID != NULL) {
+ for (i = 0; i < 16; i++) {
+ UMID_base[i] = UMID[i];
+ }
+ }
+
+ MAP_base[0] = permissions;
+
+ udelay(20);
+
+ /* Check that the partition was engaged correctly, and that it has the
+ * proper permissions.
+ */
+
+ if ((!partition_engaged(partition_no)) ||
+ (permissions !=
+ SCC_READ_REGISTER(SCM_ACC0_REG + 8 * partition_no))) {
+ return SCC_RET_FAIL;
+ }
+
+ return SCC_RET_OK;
+}
+
+/*****************************************************************************/
+/* fn scc_init() */
+/*****************************************************************************/
+/**
+ * Initialize the driver at boot time or module load time.
+ *
+ * Register with the kernel as the interrupt handler for the SCC interrupt
+ * line(s).
+ *
+ * Map the SCC's register space into the driver's memory space.
+ *
+ * Query the SCC for its configuration and status. Save the configuration in
+ * #scc_configuration and save the status in #scc_availability. Called by the
+ * kernel.
+ *
+ * Do any locking/wait queue initialization which may be necessary.
+ *
+ * The availability fuse may be checked, depending on platform.
+ */
+static int scc_init(void)
+{
+ uint32_t smn_status;
+ int i;
+ int return_value = -EIO; /* assume error */
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+
+ /* Set this until we get an initial reading */
+ scc_availability = SCC_STATUS_CHECKING;
+
+ /* Initialize the constant for the CRC function */
+ scc_init_ccitt_crc();
+
+ /* initialize the callback table */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ scc_callbacks[i] = 0;
+ }
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
+ mxc_clks_enable(SCC_CLK);
+#else
+ if (IS_ERR(scc_clk)) {
+ return_value = PTR_ERR(scc_clk);
+ goto out;
+ } else
+ clk_enable(scc_clk);
+#endif
+
+ /* Set up the hardware access locks */
+ scc_callbacks_lock = os_lock_alloc_init();
+ scc_crypto_lock = os_lock_alloc_init();
+ if (scc_callbacks_lock == NULL || scc_crypto_lock == NULL) {
+ os_printk(KERN_ERR
+ "SCC2: Failed to allocate context locks. Exiting.\n");
+ goto out;
+ }
+
+ /* See whether there is an SCC available */
+ if (0 && !SCC_ENABLED()) {
+ os_printk(KERN_ERR
+ "SCC2: Fuse for SCC is set to disabled. Exiting.\n");
+ goto out;
+ }
+ /* Map the SCC (SCM and SMN) memory on the internal bus into
+ kernel address space */
+ scc_base = (void *)ioremap(scc_phys_base, SZ_4K);
+ if (scc_base == NULL) {
+ os_printk(KERN_ERR
+ "SCC2: Register mapping failed. Exiting.\n");
+ goto out;
+ }
+
+ /* If that worked, we can try to use the SCC */
+ /* Get SCM into 'clean' condition w/interrupts cleared &
+ disabled */
+ SCC_WRITE_REGISTER(SCM_INT_CTL_REG, 0);
+
+ /* Clear error status register */
+ (void)SCC_READ_REGISTER(SCM_ERR_STATUS_REG);
+
+ /*
+ * There is an SCC. Determine its current state. Side effect
+ * is to populate scc_config and scc_availability
+ */
+ smn_status = scc_grab_config_values();
+
+ /* Try to set up interrupt handler(s) */
+ if (scc_availability != SCC_STATUS_OK) {
+ goto out;
+ }
+
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) < 0)
+ scm_ram_phys_base += 0x8000;
+
+ scm_ram_base = (void *)ioremap_nocache(scm_ram_phys_base,
+ scc_configuration.
+ partition_count *
+ scc_configuration.
+ partition_size_bytes);
+ if (scm_ram_base == NULL) {
+ os_printk(KERN_ERR
+ "SCC2: RAM failed to remap: %p for %d bytes\n",
+ (void *)scm_ram_phys_base,
+ scc_configuration.partition_count *
+ scc_configuration.partition_size_bytes);
+ goto out;
+ }
+ pr_debug("SCC2: RAM at Physical %p / Virtual %p\n",
+ (void *)scm_ram_phys_base, scm_ram_base);
+
+ pr_debug("Secure Partition Table: Found %i partitions\n",
+ scc_configuration.partition_count);
+
+ if (setup_interrupt_handling() != 0) {
+ unsigned err_cond;
+ /**
+ * The error could be only that the SCM interrupt was
+ * not set up. This interrupt is always masked, so
+ * that is not an issue.
+ * The SMN's interrupt may be shared on that line, it
+ * may be separate, or it may not be wired. Do what
+ * is necessary to check its status.
+ * Although the driver is coded for possibility of not
+ * having SMN interrupt, the fact that there is one
+ * means it should be available and used.
+ */
+#ifdef USE_SMN_INTERRUPT
+ err_cond = !smn_irq_set; /* Separate. Check SMN binding */
+#elif !defined(NO_SMN_INTERRUPT)
+ err_cond = !scm_irq_set; /* Shared. Check SCM binding */
+#else
+ err_cond = FALSE; /* SMN not wired at all. Ignore. */
+#endif
+ if (err_cond) {
+ /* setup was not able to set up SMN interrupt */
+ scc_availability = SCC_STATUS_UNIMPLEMENTED;
+ goto out;
+ }
+ }
+
+ /* interrupt handling returned non-zero */
+ /* Get SMN into 'clean' condition w/interrupts cleared &
+ enabled */
+ SCC_WRITE_REGISTER(SMN_COMMAND_REG,
+ SMN_COMMAND_CLEAR_INTERRUPT
+ | SMN_COMMAND_ENABLE_INTERRUPT);
+
+ out:
+ /*
+ * If status is SCC_STATUS_UNIMPLEMENTED or is still
+ * SCC_STATUS_CHECKING, could be leaving here with the driver partially
+ * initialized. In either case, cleanup (which will mark the SCC as
+ * UNIMPLEMENTED).
+ */
+ if (scc_availability == SCC_STATUS_CHECKING ||
+ scc_availability == SCC_STATUS_UNIMPLEMENTED) {
+ scc_cleanup();
+ } else {
+ return_value = 0; /* All is well */
+ }
+ }
+ /* ! STATUS_INITIAL */
+ os_printk(KERN_ALERT "SCC2: Driver Status is %s\n",
+ (scc_availability == SCC_STATUS_INITIAL) ? "INITIAL" :
+ (scc_availability == SCC_STATUS_CHECKING) ? "CHECKING" :
+ (scc_availability ==
+ SCC_STATUS_UNIMPLEMENTED) ? "UNIMPLEMENTED"
+ : (scc_availability ==
+ SCC_STATUS_OK) ? "OK" : (scc_availability ==
+ SCC_STATUS_FAILED) ? "FAILED" :
+ "UNKNOWN");
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ mxc_clks_disable(SCC_CLK);
+#else
+ if (!IS_ERR(scc_clk))
+ clk_disable(scc_clk);
+#endif
+
+ return return_value;
+} /* scc_init */
+
+/*****************************************************************************/
+/* fn scc_cleanup() */
+/*****************************************************************************/
+/**
+ * Perform cleanup before driver/module is unloaded by setting the machine
+ * state close to what it was when the driver was loaded. This function is
+ * called when the kernel is shutting down or when this driver is being
+ * unloaded.
+ *
+ * A driver like this should probably never be unloaded, especially if there
+ * are other module relying upon the callback feature for monitoring the SCC
+ * status.
+ *
+ * In any case, cleanup the callback table (by clearing out all of the
+ * pointers). Deregister the interrupt handler(s). Unmap SCC registers.
+ *
+ * Note that this will not release any partitions that have been allocated.
+ *
+ */
+static void scc_cleanup(void)
+{
+ int i;
+
+ /******************************************************/
+
+ /* Mark the driver / SCC as unusable. */
+ scc_availability = SCC_STATUS_UNIMPLEMENTED;
+
+ /* Clear out callback table */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ scc_callbacks[i] = 0;
+ }
+
+ /* If SCC has been mapped in, clean it up and unmap it */
+ if (scc_base) {
+ /* For the SCM, disable interrupts. */
+ SCC_WRITE_REGISTER(SCM_INT_CTL_REG, 0);
+
+ /* For the SMN, clear and disable interrupts */
+ SCC_WRITE_REGISTER(SMN_COMMAND_REG,
+ SMN_COMMAND_CLEAR_INTERRUPT);
+ }
+
+ /* Now that interrupts cannot occur, disassociate driver from the interrupt
+ * lines.
+ */
+
+ /* Deregister SCM interrupt handler */
+ if (scm_irq_set) {
+ os_deregister_interrupt(INT_SCC_SCM);
+ }
+
+ /* Deregister SMN interrupt handler */
+ if (smn_irq_set) {
+#ifdef USE_SMN_INTERRUPT
+ os_deregister_interrupt(INT_SCC_SMN);
+#endif
+ }
+
+ /* Finally, release the mapped memory */
+ iounmap(scm_ram_base);
+
+ if (scc_callbacks_lock != NULL)
+ os_lock_deallocate(scc_callbacks_lock);
+
+ if (scc_crypto_lock != NULL)
+ os_lock_deallocate(scc_crypto_lock);
+
+ /*Disabling SCC Clock*/
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ mxc_clks_disable(SCC_CLK);
+#else
+ if (!IS_ERR(scc_clk))
+ clk_disable(scc_clk);
+ clk_put(scc_clk);
+#endif
+ pr_debug("SCC2 driver cleaned up.\n");
+
+} /* scc_cleanup */
+
+/*****************************************************************************/
+/* fn scc_get_configuration() */
+/*****************************************************************************/
+scc_config_t *scc_get_configuration(void)
+{
+ /*
+ * If some other driver calls scc before the kernel does, make sure that
+ * this driver's initialization is performed.
+ */
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /**
+ * If there is no SCC, yet the driver exists, the value -1 will be in
+ * the #scc_config_t fields for other than the driver versions.
+ */
+ return &scc_configuration;
+} /* scc_get_configuration */
+
+/*****************************************************************************/
+/* fn scc_zeroize_memories() */
+/*****************************************************************************/
+scc_return_t scc_zeroize_memories(void)
+{
+ scc_return_t return_status = SCC_RET_FAIL;
+
+ return return_status;
+} /* scc_zeroize_memories */
+
+/*****************************************************************************/
+/* fn scc_set_sw_alarm() */
+/*****************************************************************************/
+void scc_set_sw_alarm(void)
+{
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* Update scc_availability based on current SMN status. This might
+ * perform callbacks.
+ */
+ (void)scc_update_state();
+
+ /* if everything is OK, make it fail */
+ if (scc_availability == SCC_STATUS_OK) {
+
+ /* sound the alarm (and disable SMN interrupts */
+ SCC_WRITE_REGISTER(SMN_COMMAND_REG,
+ SMN_COMMAND_SET_SOFTWARE_ALARM);
+
+ scc_availability = SCC_STATUS_FAILED; /* Remember what we've done */
+
+ /* In case SMN interrupt is not available, tell the world */
+ scc_perform_callbacks();
+ }
+
+ return;
+} /* scc_set_sw_alarm */
+
+/*****************************************************************************/
+/* fn scc_monitor_security_failure() */
+/*****************************************************************************/
+scc_return_t scc_monitor_security_failure(void callback_func(void))
+{
+ int i;
+ os_lock_context_t irq_flags; /* for IRQ save/restore */
+ scc_return_t return_status = SCC_RET_TOO_MANY_FUNCTIONS;
+ int function_stored = FALSE;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* Acquire lock of callbacks table. Could be spin_lock_irq() if this
+ * routine were just called from base (not interrupt) level
+ */
+ os_lock_save_context(scc_callbacks_lock, irq_flags);
+
+ /* Search through table looking for empty slot */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ if (scc_callbacks[i] == callback_func) {
+ if (function_stored) {
+ /* Saved duplicate earlier. Clear this later one. */
+ scc_callbacks[i] = NULL;
+ }
+ /* Exactly one copy is now stored */
+ return_status = SCC_RET_OK;
+ break;
+ } else if (scc_callbacks[i] == NULL && !function_stored) {
+ /* Found open slot. Save it and remember */
+ scc_callbacks[i] = callback_func;
+ return_status = SCC_RET_OK;
+ function_stored = TRUE;
+ }
+ }
+
+ /* Free the lock */
+ os_unlock_restore_context(scc_callbacks_lock, irq_flags);
+
+ return return_status;
+} /* scc_monitor_security_failure */
+
+/*****************************************************************************/
+/* fn scc_stop_monitoring_security_failure() */
+/*****************************************************************************/
+void scc_stop_monitoring_security_failure(void callback_func(void))
+{
+ os_lock_context_t irq_flags; /* for IRQ save/restore */
+ int i;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* Acquire lock of callbacks table. Could be spin_lock_irq() if this
+ * routine were just called from base (not interrupt) level
+ */
+ os_lock_save_context(scc_callbacks_lock, irq_flags);
+
+ /* Search every entry of the table for this function */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ if (scc_callbacks[i] == callback_func) {
+ scc_callbacks[i] = NULL; /* found instance - clear it out */
+ break;
+ }
+ }
+
+ /* Free the lock */
+ os_unlock_restore_context(scc_callbacks_lock, irq_flags);
+
+ return;
+} /* scc_stop_monitoring_security_failure */
+
+/*****************************************************************************/
+/* fn scc_read_register() */
+/*****************************************************************************/
+scc_return_t scc_read_register(int register_offset, uint32_t * value)
+{
+ scc_return_t return_status = SCC_RET_FAIL;
+ uint32_t smn_status;
+ uint32_t scm_status;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* First layer of protection -- completely unaccessible SCC */
+ if (scc_availability != SCC_STATUS_UNIMPLEMENTED) {
+
+ /* Second layer -- that offset is valid */
+ if (register_offset != SMN_BB_DEC_REG && /* write only! */
+ check_register_offset(register_offset) == SCC_RET_OK) {
+
+ /* Get current status / update local state */
+ smn_status = scc_update_state();
+ scm_status = SCC_READ_REGISTER(SCM_STATUS_REG);
+
+ /*
+ * Third layer - verify that the register being requested is
+ * available in the current state of the SCC.
+ */
+ if ((return_status =
+ check_register_accessible(register_offset,
+ smn_status,
+ scm_status)) ==
+ SCC_RET_OK) {
+ *value = SCC_READ_REGISTER(register_offset);
+ }
+ }
+ }
+
+ return return_status;
+} /* scc_read_register */
+
+/*****************************************************************************/
+/* fn scc_write_register() */
+/*****************************************************************************/
+scc_return_t scc_write_register(int register_offset, uint32_t value)
+{
+ scc_return_t return_status = SCC_RET_FAIL;
+ uint32_t smn_status;
+ uint32_t scm_status;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* First layer of protection -- completely unaccessible SCC */
+ if (scc_availability != SCC_STATUS_UNIMPLEMENTED) {
+
+ /* Second layer -- that offset is valid */
+ if (!((register_offset == SCM_STATUS_REG) || /* These registers are */
+ (register_offset == SCM_VERSION_REG) || /* Read Only */
+ (register_offset == SMN_BB_CNT_REG) ||
+ (register_offset == SMN_TIMER_REG)) &&
+ check_register_offset(register_offset) == SCC_RET_OK) {
+
+ /* Get current status / update local state */
+ smn_status = scc_update_state();
+ scm_status = SCC_READ_REGISTER(SCM_STATUS_REG);
+
+ /*
+ * Third layer - verify that the register being requested is
+ * available in the current state of the SCC.
+ */
+ if (check_register_accessible
+ (register_offset, smn_status, scm_status) == 0) {
+ SCC_WRITE_REGISTER(register_offset, value);
+ return_status = SCC_RET_OK;
+ }
+ }
+ }
+
+ return return_status;
+} /* scc_write_register() */
+
+/******************************************************************************
+ *
+ * Function Implementations - Internal
+ *
+ *****************************************************************************/
+
+/*****************************************************************************/
+/* fn scc_irq() */
+/*****************************************************************************/
+/**
+ * This is the interrupt handler for the SCC.
+ *
+ * This function checks the SMN Status register to see whether it
+ * generated the interrupt, then it checks the SCM Status register to
+ * see whether it needs attention.
+ *
+ * If an SMN Interrupt is active, then the SCC state set to failure, and
+ * #scc_perform_callbacks() is invoked to notify any interested parties.
+ *
+ * The SCM Interrupt should be masked, as this driver uses polling to determine
+ * when the SCM has completed a crypto or zeroing operation. Therefore, if the
+ * interrupt is active, the driver will just clear the interrupt and (re)mask.
+ */
+OS_DEV_ISR(scc_irq)
+{
+ uint32_t smn_status;
+ uint32_t scm_status;
+ int handled = 0; /* assume interrupt isn't from SMN */
+#if defined(USE_SMN_INTERRUPT)
+ int smn_irq = INT_SCC_SMN; /* SMN interrupt is on a line by itself */
+#elif defined (NO_SMN_INTERRUPT)
+ int smn_irq = -1; /* not wired to CPU at all */
+#else
+ int smn_irq = INT_SCC_SCM; /* SMN interrupt shares a line with SCM */
+#endif
+
+ /* Update current state... This will perform callbacks... */
+ smn_status = scc_update_state();
+
+ /* SMN is on its own interrupt line. Verify the IRQ was triggered
+ * before clearing the interrupt and marking it handled. */
+ if ((os_dev_get_irq() == smn_irq) &&
+ (smn_status & SMN_STATUS_SMN_STATUS_IRQ)) {
+ SCC_WRITE_REGISTER(SMN_COMMAND_REG,
+ SMN_COMMAND_CLEAR_INTERRUPT);
+ handled++; /* tell kernel that interrupt was handled */
+ }
+
+ /* Check on the health of the SCM */
+ scm_status = SCC_READ_REGISTER(SCM_STATUS_REG);
+
+ /* The driver masks interrupts, so this should never happen. */
+ if (os_dev_get_irq() == INT_SCC_SCM) {
+ /* but if it does, try to prevent it in the future */
+ SCC_WRITE_REGISTER(SCM_INT_CTL_REG, 0);
+ handled++;
+ }
+
+ /* Any non-zero value of handled lets kernel know we got something */
+ os_dev_isr_return(handled);
+}
+
+/*****************************************************************************/
+/* fn scc_perform_callbacks() */
+/*****************************************************************************/
+/** Perform callbacks registered by #scc_monitor_security_failure().
+ *
+ * Make sure callbacks only happen once... Since there may be some reason why
+ * the interrupt isn't generated, this routine could be called from base(task)
+ * level.
+ *
+ * One at a time, go through #scc_callbacks[] and call any non-null pointers.
+ */
+static void scc_perform_callbacks(void)
+{
+ static int callbacks_performed = 0;
+ unsigned long irq_flags; /* for IRQ save/restore */
+ int i;
+
+ /* Acquire lock of callbacks table and callbacks_performed flag */
+ os_lock_save_context(scc_callbacks_lock, irq_flags);
+
+ if (!callbacks_performed) {
+ callbacks_performed = 1;
+
+ /* Loop over all of the entries in the table */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ /* If not null, ... */
+ if (scc_callbacks[i]) {
+ scc_callbacks[i] (); /* invoke the callback routine */
+ }
+ }
+ }
+
+ os_unlock_restore_context(scc_callbacks_lock, irq_flags);
+
+ return;
+}
+
+/*****************************************************************************/
+/* fn scc_update_state() */
+/*****************************************************************************/
+/**
+ * Make certain SCC is still running.
+ *
+ * Side effect is to update #scc_availability and, if the state goes to failed,
+ * run #scc_perform_callbacks().
+ *
+ * (If #SCC_BRINGUP is defined, bring SCC to secure state if it is found to be
+ * in health check state)
+ *
+ * @return Current value of #SMN_STATUS_REG register.
+ */
+static uint32_t scc_update_state(void)
+{
+ uint32_t smn_status_register = SMN_STATE_FAIL;
+ int smn_state;
+
+ /* if FAIL or UNIMPLEMENTED, don't bother */
+ if (scc_availability == SCC_STATUS_CHECKING ||
+ scc_availability == SCC_STATUS_OK) {
+
+ smn_status_register = SCC_READ_REGISTER(SMN_STATUS_REG);
+ smn_state = smn_status_register & SMN_STATUS_STATE_MASK;
+
+#ifdef SCC_BRINGUP
+ /* If in Health Check while booting, try to 'bringup' to Secure mode */
+ if (scc_availability == SCC_STATUS_CHECKING &&
+ smn_state == SMN_STATE_HEALTH_CHECK) {
+ /* Code up a simple algorithm for the ASC */
+ SCC_WRITE_REGISTER(SMN_SEQ_START_REG, 0xaaaa);
+ SCC_WRITE_REGISTER(SMN_SEQ_END_REG, 0x5555);
+ SCC_WRITE_REGISTER(SMN_SEQ_CHECK_REG, 0x5555);
+ /* State should be SECURE now */
+ smn_status_register = SCC_READ_REGISTER(SMN_STATUS);
+ smn_state = smn_status_register & SMN_STATUS_STATE_MASK;
+ }
+#endif
+
+ /*
+ * State should be SECURE or NON_SECURE for operation of the part. If
+ * FAIL, mark failed (i.e. limited access to registers). Any other
+ * state, mark unimplemented, as the SCC is unuseable.
+ */
+ if (smn_state == SMN_STATE_SECURE
+ || smn_state == SMN_STATE_NON_SECURE) {
+ /* Healthy */
+ scc_availability = SCC_STATUS_OK;
+ } else if (smn_state == SMN_STATE_FAIL) {
+ scc_availability = SCC_STATUS_FAILED; /* uh oh - unhealthy */
+ scc_perform_callbacks();
+ os_printk(KERN_ERR "SCC2: SCC went into FAILED mode\n");
+ } else {
+ /* START, ZEROIZE RAM, HEALTH CHECK, or unknown */
+ scc_availability = SCC_STATUS_UNIMPLEMENTED; /* unuseable */
+ os_printk(KERN_ERR
+ "SCC2: SCC declared UNIMPLEMENTED\n");
+ }
+ }
+ /* if availability is initial or ok */
+ return smn_status_register;
+}
+
+/*****************************************************************************/
+/* fn scc_init_ccitt_crc() */
+/*****************************************************************************/
+/**
+ * Populate the partial CRC lookup table.
+ *
+ * @return none
+ *
+ */
+static void scc_init_ccitt_crc(void)
+{
+ int dividend; /* index for lookup table */
+ uint16_t remainder; /* partial value for a given dividend */
+ int bit; /* index into bits of a byte */
+
+ /*
+ * Compute the remainder of each possible dividend.
+ */
+ for (dividend = 0; dividend < 256; ++dividend) {
+ /*
+ * Start with the dividend followed by zeros.
+ */
+ remainder = dividend << (8);
+
+ /*
+ * Perform modulo-2 division, a bit at a time.
+ */
+ for (bit = 8; bit > 0; --bit) {
+ /*
+ * Try to divide the current data bit.
+ */
+ if (remainder & 0x8000) {
+ remainder = (remainder << 1) ^ CRC_POLYNOMIAL;
+ } else {
+ remainder = (remainder << 1);
+ }
+ }
+
+ /*
+ * Store the result into the table.
+ */
+ scc_crc_lookup_table[dividend] = remainder;
+ }
+
+} /* scc_init_ccitt_crc() */
+
+/*****************************************************************************/
+/* fn grab_config_values() */
+/*****************************************************************************/
+/**
+ * grab_config_values() will read the SCM Configuration and SMN Status
+ * registers and store away version and size information for later use.
+ *
+ * @return The current value of the SMN Status register.
+ */
+static uint32_t scc_grab_config_values(void)
+{
+ uint32_t scm_version_register;
+ uint32_t smn_status_register = SMN_STATE_FAIL;
+
+ if (scc_availability != SCC_STATUS_CHECKING) {
+ goto out;
+ }
+ scm_version_register = SCC_READ_REGISTER(SCM_VERSION_REG);
+ pr_debug("SCC2 Driver: SCM version is 0x%08x\n", scm_version_register);
+
+ /* Get SMN status and update scc_availability */
+ smn_status_register = scc_update_state();
+ pr_debug("SCC2 Driver: SMN status is 0x%08x\n", smn_status_register);
+
+ /* save sizes and versions information for later use */
+ scc_configuration.block_size_bytes = 16; /* BPCP ? */
+ scc_configuration.partition_count =
+ 1 + ((scm_version_register & SCM_VER_NP_MASK) >> SCM_VER_NP_SHIFT);
+ scc_configuration.partition_size_bytes =
+ 1 << ((scm_version_register & SCM_VER_BPP_MASK) >>
+ SCM_VER_BPP_SHIFT);
+ scc_configuration.scm_version =
+ (scm_version_register & SCM_VER_MAJ_MASK) >> SCM_VER_MAJ_SHIFT;
+ scc_configuration.smn_version =
+ (smn_status_register & SMN_STATUS_VERSION_ID_MASK)
+ >> SMN_STATUS_VERSION_ID_SHIFT;
+ if (scc_configuration.scm_version != SCM_MAJOR_VERSION_2) {
+ scc_availability = SCC_STATUS_UNIMPLEMENTED; /* Unknown version */
+ }
+
+ out:
+ return smn_status_register;
+} /* grab_config_values */
+
+/*****************************************************************************/
+/* fn setup_interrupt_handling() */
+/*****************************************************************************/
+/**
+ * Register the SCM and SMN interrupt handlers.
+ *
+ * Called from #scc_init()
+ *
+ * @return 0 on success
+ */
+static int setup_interrupt_handling(void)
+{
+ int smn_error_code = -1;
+ int scm_error_code = -1;
+
+ /* Disnable SCM interrupts */
+ SCC_WRITE_REGISTER(SCM_INT_CTL_REG, 0);
+
+#ifdef USE_SMN_INTERRUPT
+ /* Install interrupt service routine for SMN. */
+ smn_error_code = os_register_interrupt(SCC_DRIVER_NAME,
+ INT_SCC_SMN, scc_irq);
+ if (smn_error_code != 0) {
+ os_printk(KERN_ERR
+ "SCC2 Driver: Error installing SMN Interrupt Handler: %d\n",
+ smn_error_code);
+ } else {
+ smn_irq_set = 1; /* remember this for cleanup */
+ /* Enable SMN interrupts */
+ SCC_WRITE_REGISTER(SMN_COMMAND_REG,
+ SMN_COMMAND_CLEAR_INTERRUPT |
+ SMN_COMMAND_ENABLE_INTERRUPT);
+ }
+#else
+ smn_error_code = 0; /* no problems... will handle later */
+#endif
+
+ /*
+ * Install interrupt service routine for SCM (or both together).
+ */
+ scm_error_code = os_register_interrupt(SCC_DRIVER_NAME,
+ INT_SCC_SCM, scc_irq);
+ if (scm_error_code != 0) {
+#ifndef MXC
+ os_printk(KERN_ERR
+ "SCC2 Driver: Error installing SCM Interrupt Handler: %d\n",
+ scm_error_code);
+#else
+ os_printk(KERN_ERR
+ "SCC2 Driver: Error installing SCC Interrupt Handler: %d\n",
+ scm_error_code);
+#endif
+ } else {
+ scm_irq_set = 1; /* remember this for cleanup */
+#if defined(USE_SMN_INTERRUPT) && !defined(NO_SMN_INTERRUPT)
+ /* Enable SMN interrupts */
+ SCC_WRITE_REGISTER(SMN_COMMAND_REG,
+ SMN_COMMAND_CLEAR_INTERRUPT |
+ SMN_COMMAND_ENABLE_INTERRUPT);
+#endif
+ }
+
+ /* Return an error if one was encountered */
+ return scm_error_code ? scm_error_code : smn_error_code;
+} /* setup_interrupt_handling */
+
+/*****************************************************************************/
+/* fn scc_do_crypto() */
+/*****************************************************************************/
+/** Have the SCM perform the crypto function.
+ *
+ * Set up length register, and the store @c scm_control into control register
+ * to kick off the operation. Wait for completion, gather status, clear
+ * interrupt / status.
+ *
+ * @param byte_count number of bytes to perform in this operation
+ * @param scm_command Bit values to be set in @c SCM_CCMD_REG register
+ *
+ * @return 0 on success, value of #SCM_ERR_STATUS_REG on failure
+ */
+static uint32_t scc_do_crypto(int byte_count, uint32_t scm_command)
+{
+ int block_count = byte_count / SCC_BLOCK_SIZE_BYTES();
+ uint32_t crypto_status;
+ scc_return_t ret;
+
+ /* This seems to be necessary in order to allow subsequent cipher
+ * operations to succeed when a partition is deallocated/reallocated!
+ */
+ (void)SCC_READ_REGISTER(SCM_STATUS_REG);
+
+ /* In length register, 0 means 1, etc. */
+ scm_command |= (block_count - 1) << SCM_CCMD_LENGTH_SHIFT;
+
+ /* set modes and kick off the operation */
+ SCC_WRITE_REGISTER(SCM_CCMD_REG, scm_command);
+
+ ret = scc_wait_completion(&crypto_status);
+
+ /* Only done bit should be on */
+ if (crypto_status & SCM_STATUS_ERR) {
+ /* Replace with error status instead */
+ crypto_status = SCC_READ_REGISTER(SCM_ERR_STATUS_REG);
+ pr_debug("SCM Failure: 0x%x\n", crypto_status);
+ if (crypto_status == 0) {
+ /* That came up 0. Turn on arbitrary bit to signal error. */
+ crypto_status = SCM_ERRSTAT_ILM;
+ }
+ } else {
+ crypto_status = 0;
+ }
+ pr_debug("SCC2: Done waiting.\n");
+
+ return crypto_status;
+}
+
+/**
+ * Encrypt a region of secure memory.
+ *
+ * @param part_base Kernel virtual address of the partition.
+ * @param offset_bytes Offset from the start of the partition to the plaintext
+ * data.
+ * @param byte_count Length of the region (octets).
+ * @param black_data Physical location to store the encrypted data.
+ * @param IV Value to use for the IV.
+ * @param cypher_mode Cyphering mode to use, specified by type
+ * #scc_cypher_mode_t
+ *
+ * @return SCC_RET_OK if successful.
+ */
+scc_return_t
+scc_encrypt_region(uint32_t part_base, uint32_t offset_bytes,
+ uint32_t byte_count, uint8_t *black_data,
+ uint32_t *IV, scc_cypher_mode_t cypher_mode)
+{
+ os_lock_context_t irq_flags; /* for IRQ save/restore */
+ scc_return_t status = SCC_RET_OK;
+ uint32_t crypto_status;
+ uint32_t scm_command;
+ int offset_blocks = offset_bytes / SCC_BLOCK_SIZE_BYTES();
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ mxc_clks_enable(SCC_CLK);
+#else
+ if (IS_ERR(scc_clk)) {
+ status = SCC_RET_FAIL;
+ goto out;
+ } else
+ clk_enable(scc_clk);
+#endif
+
+ scm_command = ((offset_blocks << SCM_CCMD_OFFSET_SHIFT) |
+ (SCM_PART_NUMBER(part_base) << SCM_CCMD_PART_SHIFT));
+
+ switch (cypher_mode) {
+ case SCC_CYPHER_MODE_CBC:
+ scm_command |= SCM_CCMD_AES_ENC_CBC;
+ break;
+ case SCC_CYPHER_MODE_ECB:
+ scm_command |= SCM_CCMD_AES_ENC_ECB;
+ break;
+ default:
+ status = SCC_RET_FAIL;
+ break;
+ }
+
+ pr_debug("Received encrypt request. SCM_C_BLACK_ST_REG: %p, "
+ "scm_Command: %08x, length: %i (part_base: %08x, "
+ "offset: %i)\n",
+ black_data, scm_command, byte_count, part_base, offset_blocks);
+
+ if (status != SCC_RET_OK)
+ goto out;
+
+ /* ACQUIRE LOCK to prevent others from using crypto or releasing slot */
+ os_lock_save_context(scc_crypto_lock, irq_flags);
+
+ if (status == SCC_RET_OK) {
+ SCC_WRITE_REGISTER(SCM_C_BLACK_ST_REG, (uint32_t) black_data);
+
+ /* Only write the IV if it will actually be used */
+ if (cypher_mode == SCC_CYPHER_MODE_CBC) {
+ /* Write the IV register */
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV0_REG, *(IV));
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV1_REG, *(IV + 1));
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV2_REG, *(IV + 2));
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV3_REG, *(IV + 3));
+ }
+
+ /* Set modes and kick off the encryption */
+ crypto_status = scc_do_crypto(byte_count, scm_command);
+
+ if (crypto_status != 0) {
+ pr_debug("SCM encrypt red crypto failure: 0x%x\n",
+ crypto_status);
+ } else {
+ status = SCC_RET_OK;
+ pr_debug("SCC2: Encrypted %d bytes\n", byte_count);
+ }
+ }
+
+ os_unlock_restore_context(scc_crypto_lock, irq_flags);
+
+out:
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ mxc_clks_disable(SCC_CLK);
+#else
+ if (!IS_ERR(scc_clk))
+ clk_disable(scc_clk);
+#endif
+
+ return status;
+}
+
+/* Decrypt a region into secure memory
+ *
+ * @param part_base Kernel virtual address of the partition.
+ * @param offset_bytes Offset from the start of the partition to store the
+ * plaintext data.
+ * @param byte_counts Length of the region (octets).
+ * @param black_data Physical location of the encrypted data.
+ * @param IV Value to use for the IV.
+ * @param cypher_mode Cyphering mode to use, specified by type
+ * #scc_cypher_mode_t
+ *
+ * @return SCC_RET_OK if successful.
+ */
+scc_return_t
+scc_decrypt_region(uint32_t part_base, uint32_t offset_bytes,
+ uint32_t byte_count, uint8_t *black_data,
+ uint32_t *IV, scc_cypher_mode_t cypher_mode)
+{
+ os_lock_context_t irq_flags; /* for IRQ save/restore */
+ scc_return_t status = SCC_RET_OK;
+ uint32_t crypto_status;
+ uint32_t scm_command;
+ int offset_blocks = offset_bytes / SCC_BLOCK_SIZE_BYTES();
+
+ /*Enabling SCC clock.*/
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ mxc_clks_enable(SCC_CLK);
+#else
+ if (IS_ERR(scc_clk)) {
+ status = SCC_RET_FAIL;
+ goto out;
+ } else
+ clk_enable(scc_clk);
+#endif
+ scm_command = ((offset_blocks << SCM_CCMD_OFFSET_SHIFT) |
+ (SCM_PART_NUMBER(part_base) << SCM_CCMD_PART_SHIFT));
+
+ switch (cypher_mode) {
+ case SCC_CYPHER_MODE_CBC:
+ scm_command |= SCM_CCMD_AES_DEC_CBC;
+ break;
+ case SCC_CYPHER_MODE_ECB:
+ scm_command |= SCM_CCMD_AES_DEC_ECB;
+ break;
+ default:
+ status = SCC_RET_FAIL;
+ break;
+ }
+
+ pr_debug("Received decrypt request. SCM_C_BLACK_ST_REG: %p, "
+ "scm_Command: %08x, length: %i (part_base: %08x, "
+ "offset: %i)\n",
+ black_data, scm_command, byte_count, part_base, offset_blocks);
+
+ if (status != SCC_RET_OK)
+ goto out;
+
+ /* ACQUIRE LOCK to prevent others from using crypto or releasing slot */
+ os_lock_save_context(scc_crypto_lock, irq_flags);
+
+ if (status == SCC_RET_OK) {
+ status = SCC_RET_FAIL; /* reset expectations */
+ SCC_WRITE_REGISTER(SCM_C_BLACK_ST_REG, (uint32_t) black_data);
+
+ /* Write the IV register */
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV0_REG, *(IV));
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV1_REG, *(IV + 1));
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV2_REG, *(IV + 2));
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV3_REG, *(IV + 3));
+
+ /* Set modes and kick off the decryption */
+ crypto_status = scc_do_crypto(byte_count, scm_command);
+
+ if (crypto_status != 0) {
+ pr_debug("SCM decrypt black crypto failure: 0x%x\n",
+ crypto_status);
+ } else {
+ status = SCC_RET_OK;
+ pr_debug("SCC2: Decrypted %d bytes\n", byte_count);
+ }
+ }
+
+ os_unlock_restore_context(scc_crypto_lock, irq_flags);
+out:
+ /*Disabling the Clock when the driver is not in use.*/
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ mxc_clks_disable(SCC_CLK);
+#else
+ if (!IS_ERR(scc_clk))
+ clk_disable(scc_clk);
+#endif
+ return status;
+}
+
+/*****************************************************************************/
+/* fn host_owns_partition() */
+/*****************************************************************************/
+/**
+ * Determine if the host owns a given partition.
+ *
+ * @internal
+ *
+ * @param part_no Partition number to query
+ *
+ * @return TRUE if the host owns the partition, FALSE otherwise.
+ */
+
+static uint32_t host_owns_partition(uint32_t part_no)
+{
+ uint32_t value;
+
+ if (part_no < scc_configuration.partition_count) {
+
+ /* Check the partition owners register */
+ value = SCC_READ_REGISTER(SCM_PART_OWNERS_REG);
+ if (((value >> (part_no * SCM_POWN_SHIFT)) & SCM_POWN_MASK)
+ == SCM_POWN_PART_OWNED)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*****************************************************************************/
+/* fn partition_engaged() */
+/*****************************************************************************/
+/**
+ * Determine if the given partition is engaged.
+ *
+ * @internal
+ *
+ * @param part_no Partition number to query
+ *
+ * @return TRUE if the partition is engaged, FALSE otherwise.
+ */
+
+static uint32_t partition_engaged(uint32_t part_no)
+{
+ uint32_t value;
+
+ if (part_no < scc_configuration.partition_count) {
+
+ /* Check the partition engaged register */
+ value = SCC_READ_REGISTER(SCM_PART_ENGAGED_REG);
+ if (((value >> (part_no * SCM_PENG_SHIFT)) & 0x1)
+ == SCM_PENG_ENGAGED)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*****************************************************************************/
+/* fn scc_wait_completion() */
+/*****************************************************************************/
+/**
+ * Poll looking for end-of-cipher indication. Only used
+ * if @c SCC_SCM_SLEEP is not defined.
+ *
+ * @internal
+ *
+ * On a Tahiti, crypto under 230 or so bytes is done after the first loop, all
+ * the way up to five sets of spins for 1024 bytes. (8- and 16-byte functions
+ * are done when we first look. Zeroizing takes one pass around.
+ *
+ * @param scm_status Address of the SCM_STATUS register
+ *
+ * @return A return code of type #scc_return_t
+ */
+static scc_return_t scc_wait_completion(uint32_t * scm_status)
+{
+ scc_return_t ret;
+ int done;
+ int i = 0;
+
+ /* check for completion by polling */
+ do {
+ done = is_cipher_done(scm_status);
+ if (done)
+ break;
+ /* TODO: shorten this delay */
+ udelay(1000);
+ } while (i++ < SCC_CIPHER_MAX_POLL_COUNT);
+
+ pr_debug("SCC2: Polled DONE %d times\n", i);
+ if (!done) {
+ ret = SCC_RET_FAIL;
+ }
+
+ return ret;
+} /* scc_wait_completion() */
+
+/*****************************************************************************/
+/* fn is_cipher_done() */
+/*****************************************************************************/
+/**
+ * This function returns non-zero if SCM Status register indicates
+ * that a cipher has terminated or some other interrupt-generating
+ * condition has occurred.
+ *
+ * @param scm_status Address of the SCM STATUS register
+ *
+ * @return 0 if cipher operations are finished
+ */
+static int is_cipher_done(uint32_t * scm_status)
+{
+ register unsigned status;
+ register int cipher_done;
+
+ *scm_status = SCC_READ_REGISTER(SCM_STATUS_REG);
+ status = (*scm_status & SCM_STATUS_SRS_MASK) >> SCM_STATUS_SRS_SHIFT;
+
+ /*
+ * Done when SCM is not in 'currently performing a function' states.
+ */
+ cipher_done = ((status != SCM_STATUS_SRS_ZBUSY)
+ && (status != SCM_STATUS_SRS_CBUSY)
+ && (status != SCM_STATUS_SRS_ABUSY));
+
+ return cipher_done;
+} /* is_cipher_done() */
+
+/*****************************************************************************/
+/* fn offset_within_smn() */
+/*****************************************************************************/
+/*!
+ * Check that the offset is with the bounds of the SMN register set.
+ *
+ * @param[in] register_offset register offset of SMN.
+ *
+ * @return 1 if true, 0 if false (not within SMN)
+ */
+static inline int offset_within_smn(uint32_t register_offset)
+{
+ return ((register_offset >= SMN_STATUS_REG)
+ && (register_offset <= SMN_HAC_REG));
+}
+
+/*****************************************************************************/
+/* fn offset_within_scm() */
+/*****************************************************************************/
+/*!
+ * Check that the offset is with the bounds of the SCM register set.
+ *
+ * @param[in] register_offset Register offset of SCM
+ *
+ * @return 1 if true, 0 if false (not within SCM)
+ */
+static inline int offset_within_scm(uint32_t register_offset)
+{
+ return 1; /* (register_offset >= SCM_RED_START)
+ && (register_offset < scm_highest_memory_address); */
+/* Although this would cause trouble for zeroize testing, this change would
+ * close a security hole which currently allows any kernel program to access
+ * any location in RED RAM. Perhaps enforce in non-SCC_DEBUG compiles?
+ && (register_offset <= SCM_INIT_VECTOR_1); */
+}
+
+/*****************************************************************************/
+/* fn check_register_accessible() */
+/*****************************************************************************/
+/**
+ * Given the current SCM and SMN status, verify that access to the requested
+ * register should be OK.
+ *
+ * @param[in] register_offset register offset within SCC
+ * @param[in] smn_status recent value from #SMN_STATUS_REG
+ * @param[in] scm_status recent value from #SCM_STATUS_REG
+ *
+ * @return #SCC_RET_OK if ok, #SCC_RET_FAIL if not
+ */
+static scc_return_t
+check_register_accessible(uint32_t register_offset, uint32_t smn_status,
+ uint32_t scm_status)
+{
+ int error_code = SCC_RET_FAIL;
+
+ /* Verify that the register offset passed in is not among the verboten set
+ * if the SMN is in Fail mode.
+ */
+ if (offset_within_smn(register_offset)) {
+ if ((smn_status & SMN_STATUS_STATE_MASK) == SMN_STATE_FAIL) {
+ if (!((register_offset == SMN_STATUS_REG) ||
+ (register_offset == SMN_COMMAND_REG) ||
+ (register_offset == SMN_SEC_VIO_REG))) {
+ pr_debug
+ ("SCC2 Driver: Note: Security State is in FAIL state.\n");
+ } /* register not a safe one */
+ else {
+ /* SMN is in FAIL, but register is a safe one */
+ error_code = SCC_RET_OK;
+ }
+ } /* State is FAIL */
+ else {
+ /* State is not fail. All registers accessible. */
+ error_code = SCC_RET_OK;
+ }
+ }
+ /* offset within SMN */
+ /* Not SCM register. Check for SCM busy. */
+ else if (offset_within_scm(register_offset)) {
+ /* This is the 'cannot access' condition in the SCM */
+ if (0 /* (scm_status & SCM_STATUS_BUSY) */
+ /* these are always available - rest fail on busy */
+ && !((register_offset == SCM_STATUS_REG) ||
+ (register_offset == SCM_ERR_STATUS_REG) ||
+ (register_offset == SCM_INT_CTL_REG) ||
+ (register_offset == SCM_VERSION_REG))) {
+ pr_debug
+ ("SCC2 Driver: Note: Secure Memory is in BUSY state.\n");
+ } /* status is busy & register inaccessible */
+ else {
+ error_code = SCC_RET_OK;
+ }
+ }
+ /* offset within SCM */
+ return error_code;
+
+} /* check_register_accessible() */
+
+/*****************************************************************************/
+/* fn check_register_offset() */
+/*****************************************************************************/
+/**
+ * Check that the offset is with the bounds of the SCC register set.
+ *
+ * @param[in] register_offset register offset of SMN.
+ *
+ * #SCC_RET_OK if ok, #SCC_RET_FAIL if not
+ */
+static scc_return_t check_register_offset(uint32_t register_offset)
+{
+ int return_value = SCC_RET_FAIL;
+
+ /* Is it valid word offset ? */
+ if (SCC_BYTE_OFFSET(register_offset) == 0) {
+ /* Yes. Is register within SCM? */
+ if (offset_within_scm(register_offset)) {
+ return_value = SCC_RET_OK; /* yes, all ok */
+ }
+ /* Not in SCM. Now look within the SMN */
+ else if (offset_within_smn(register_offset)) {
+ return_value = SCC_RET_OK; /* yes, all ok */
+ }
+ }
+
+ return return_value;
+}
+
+#ifdef SCC_REGISTER_DEBUG
+
+/**
+ * Names of the SCC Registers, indexed by register number
+ */
+static char *scc_regnames[] = {
+ "SCM_VERSION_REG",
+ "0x04",
+ "SCM_INT_CTL_REG",
+ "SCM_STATUS_REG",
+ "SCM_ERR_STATUS_REG",
+ "SCM_FAULT_ADR_REG",
+ "SCM_PART_OWNERS_REG",
+ "SCM_PART_ENGAGED_REG",
+ "SCM_UNIQUE_ID0_REG",
+ "SCM_UNIQUE_ID1_REG",
+ "SCM_UNIQUE_ID2_REG",
+ "SCM_UNIQUE_ID3_REG",
+ "0x30",
+ "0x34",
+ "0x38",
+ "0x3C",
+ "0x40",
+ "0x44",
+ "0x48",
+ "0x4C",
+ "SCM_ZCMD_REG",
+ "SCM_CCMD_REG",
+ "SCM_C_BLACK_ST_REG",
+ "SCM_DBG_STATUS_REG",
+ "SCM_AES_CBC_IV0_REG",
+ "SCM_AES_CBC_IV1_REG",
+ "SCM_AES_CBC_IV2_REG",
+ "SCM_AES_CBC_IV3_REG",
+ "0x70",
+ "0x74",
+ "0x78",
+ "0x7C",
+ "SCM_SMID0_REG",
+ "SCM_ACC0_REG",
+ "SCM_SMID1_REG",
+ "SCM_ACC1_REG",
+ "SCM_SMID2_REG",
+ "SCM_ACC2_REG",
+ "SCM_SMID3_REG",
+ "SCM_ACC3_REG",
+ "SCM_SMID4_REG",
+ "SCM_ACC4_REG",
+ "SCM_SMID5_REG",
+ "SCM_ACC5_REG",
+ "SCM_SMID6_REG",
+ "SCM_ACC6_REG",
+ "SCM_SMID7_REG",
+ "SCM_ACC7_REG",
+ "SCM_SMID8_REG",
+ "SCM_ACC8_REG",
+ "SCM_SMID9_REG",
+ "SCM_ACC9_REG",
+ "SCM_SMID10_REG",
+ "SCM_ACC10_REG",
+ "SCM_SMID11_REG",
+ "SCM_ACC11_REG",
+ "SCM_SMID12_REG",
+ "SCM_ACC12_REG",
+ "SCM_SMID13_REG",
+ "SCM_ACC13_REG",
+ "SCM_SMID14_REG",
+ "SCM_ACC14_REG",
+ "SCM_SMID15_REG",
+ "SCM_ACC15_REG",
+ "SMN_STATUS_REG",
+ "SMN_COMMAND_REG",
+ "SMN_SEQ_START_REG",
+ "SMN_SEQ_END_REG",
+ "SMN_SEQ_CHECK_REG",
+ "SMN_BB_CNT_REG",
+ "SMN_BB_INC_REG",
+ "SMN_BB_DEC_REG",
+ "SMN_COMPARE_REG",
+ "SMN_PT_CHK_REG",
+ "SMN_CT_CHK_REG",
+ "SMN_TIMER_IV_REG",
+ "SMN_TIMER_CTL_REG",
+ "SMN_SEC_VIO_REG",
+ "SMN_TIMER_REG",
+ "SMN_HAC_REG"
+};
+
+/**
+ * Names of the Secure RAM States
+ */
+static char *srs_names[] = {
+ "SRS_Reset",
+ "SRS_All_Ready",
+ "SRS_ZeroizeBusy",
+ "SRS_CipherBusy",
+ "SRS_AllBusy",
+ "SRS_ZeroizeDoneCipherReady",
+ "SRS_CipherDoneZeroizeReady",
+ "SRS_ZeroizeDoneCipherBusy",
+ "SRS_CipherDoneZeroizeBusy",
+ "SRS_UNKNOWN_STATE_9",
+ "SRS_TransitionalA",
+ "SRS_TransitionalB",
+ "SRS_TransitionalC",
+ "SRS_TransitionalD",
+ "SRS_AllDone",
+ "SRS_UNKNOWN_STATE_E",
+ "SRS_FAIL"
+};
+
+/**
+ * Create a text interpretation of the SCM Version Register
+ *
+ * @param value The value of the register
+ * @param[out] print_buffer Place to store the interpretation
+ * @param buf_size Number of bytes available at print_buffer
+ *
+ * @return The print_buffer
+ */
+static
+char *scm_print_version_reg(uint32_t value, char *print_buffer, int buf_size)
+{
+ snprintf(print_buffer, buf_size,
+ "Bpp: %u, Bpcb: %u, np: %u, maj: %u, min: %u",
+ (value & SCM_VER_BPP_MASK) >> SCM_VER_BPP_SHIFT,
+ ((value & SCM_VER_BPCB_MASK) >> SCM_VER_BPCB_SHIFT) + 1,
+ ((value & SCM_VER_NP_MASK) >> SCM_VER_NP_SHIFT) + 1,
+ (value & SCM_VER_MAJ_MASK) >> SCM_VER_MAJ_SHIFT,
+ (value & SCM_VER_MIN_MASK) >> SCM_VER_MIN_SHIFT);
+
+ return print_buffer;
+}
+
+/**
+ * Create a text interpretation of the SCM Status Register
+ *
+ * @param value The value of the register
+ * @param[out] print_buffer Place to store the interpretation
+ * @param buf_size Number of bytes available at print_buffer
+ *
+ * @return The print_buffer
+ */
+static
+char *scm_print_status_reg(uint32_t value, char *print_buffer, int buf_size)
+{
+
+ snprintf(print_buffer, buf_size, "%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ (value & SCM_STATUS_KST_DEFAULT_KEY) ? "KST_DefaultKey " : "",
+ /* reserved */
+ (value & SCM_STATUS_KST_WRONG_KEY) ? "KST_WrongKey " : "",
+ (value & SCM_STATUS_KST_BAD_KEY) ? "KST_BadKey " : "",
+ (value & SCM_STATUS_ERR) ? "Error " : "",
+ (value & SCM_STATUS_MSS_FAIL) ? "MSS_FailState " : "",
+ (value & SCM_STATUS_MSS_SEC) ? "MSS_SecureState " : "",
+ (value & SCM_STATUS_RSS_FAIL) ? "RSS_FailState " : "",
+ (value & SCM_STATUS_RSS_SEC) ? "RSS_SecureState " : "",
+ (value & SCM_STATUS_RSS_INIT) ? "RSS_Initializing " : "",
+ (value & SCM_STATUS_UNV) ? "UID_Invalid " : "",
+ (value & SCM_STATUS_BIG) ? "BigEndian " : "",
+ (value & SCM_STATUS_USK) ? "SecretKey " : "",
+ srs_names[(value & SCM_STATUS_SRS_MASK) >>
+ SCM_STATUS_SRS_SHIFT]);
+
+ return print_buffer;
+}
+
+/**
+ * Names of the SCM Error Codes
+ */
+static
+char *scm_err_code[] = {
+ "Unknown_0",
+ "UnknownAddress",
+ "UnknownCommand",
+ "ReadPermErr",
+ "WritePermErr",
+ "DMAErr",
+ "EncBlockLenOvfl",
+ "KeyNotEngaged",
+ "ZeroizeCmdQOvfl",
+ "CipherCmdQOvfl",
+ "ProcessIntr",
+ "WrongKey",
+ "DeviceBusy",
+ "DMAUnalignedAddr",
+ "Unknown_E",
+ "Unknown_F",
+};
+
+/**
+ * Names of the SMN States
+ */
+static char *smn_state_name[] = {
+ "Start",
+ "Invalid_01",
+ "Invalid_02",
+ "Invalid_03",
+ "Zeroizing_04",
+ "Zeroizing",
+ "HealthCheck",
+ "HealthCheck_07",
+ "Invalid_08",
+ "Fail",
+ "Secure",
+ "Invalid_0B",
+ "NonSecure",
+ "Invalid_0D",
+ "Invalid_0E",
+ "Invalid_0F",
+ "Invalid_10",
+ "Invalid_11",
+ "Invalid_12",
+ "Invalid_13",
+ "Invalid_14",
+ "Invalid_15",
+ "Invalid_16",
+ "Invalid_17",
+ "Invalid_18",
+ "FailHard",
+ "Invalid_1A",
+ "Invalid_1B",
+ "Invalid_1C",
+ "Invalid_1D",
+ "Invalid_1E",
+ "Invalid_1F"
+};
+
+/**
+ * Create a text interpretation of the SCM Error Status Register
+ *
+ * @param value The value of the register
+ * @param[out] print_buffer Place to store the interpretation
+ * @param buf_size Number of bytes available at print_buffer
+ *
+ * @return The print_buffer
+ */
+static
+char *scm_print_err_status_reg(uint32_t value, char *print_buffer, int buf_size)
+{
+ snprintf(print_buffer, buf_size,
+ "MID: 0x%x, %s%s ErrorCode: %s, SMSState: %s, SCMState: %s",
+ (value & SCM_ERRSTAT_MID_MASK) >> SCM_ERRSTAT_MID_SHIFT,
+ (value & SCM_ERRSTAT_ILM) ? "ILM, " : "",
+ (value & SCM_ERRSTAT_SUP) ? "SUP, " : "",
+ scm_err_code[(value & SCM_ERRSTAT_ERC_MASK) >>
+ SCM_ERRSTAT_ERC_SHIFT],
+ smn_state_name[(value & SCM_ERRSTAT_SMS_MASK) >>
+ SCM_ERRSTAT_SMS_SHIFT],
+ srs_names[(value & SCM_ERRSTAT_SRS_MASK) >>
+ SCM_ERRSTAT_SRS_SHIFT]);
+ return print_buffer;
+}
+
+/**
+ * Create a text interpretation of the SCM Zeroize Command Register
+ *
+ * @param value The value of the register
+ * @param[out] print_buffer Place to store the interpretation
+ * @param buf_size Number of bytes available at print_buffer
+ *
+ * @return The print_buffer
+ */
+static
+char *scm_print_zcmd_reg(uint32_t value, char *print_buffer, int buf_size)
+{
+ unsigned cmd = (value & SCM_ZCMD_CCMD_MASK) >> SCM_CCMD_CCMD_SHIFT;
+
+ snprintf(print_buffer, buf_size, "%s %u",
+ (cmd ==
+ ZCMD_DEALLOC_PART) ? "DeallocPartition" :
+ "(unknown function)",
+ (value & SCM_ZCMD_PART_MASK) >> SCM_ZCMD_PART_SHIFT);
+
+ return print_buffer;
+}
+
+/**
+ * Create a text interpretation of the SCM Cipher Command Register
+ *
+ * @param value The value of the register
+ * @param[out] print_buffer Place to store the interpretation
+ * @param buf_size Number of bytes available at print_buffer
+ *
+ * @return The print_buffer
+ */
+static
+char *scm_print_ccmd_reg(uint32_t value, char *print_buffer, int buf_size)
+{
+ unsigned cmd = (value & SCM_CCMD_CCMD_MASK) >> SCM_CCMD_CCMD_SHIFT;
+
+ snprintf(print_buffer, buf_size,
+ "%s %u bytes, %s offset 0x%x, in partition %u",
+ (cmd == SCM_CCMD_AES_DEC_ECB) ? "ECB Decrypt" : (cmd ==
+ SCM_CCMD_AES_ENC_ECB)
+ ? "ECB Encrypt" : (cmd ==
+ SCM_CCMD_AES_DEC_CBC) ? "CBC Decrypt" : (cmd
+ ==
+ SCM_CCMD_AES_ENC_CBC)
+ ? "CBC Encrypt" : "(unknown function)",
+ 16 +
+ 16 * ((value & SCM_CCMD_LENGTH_MASK) >> SCM_CCMD_LENGTH_SHIFT),
+ ((cmd == SCM_CCMD_AES_ENC_CBC)
+ || (cmd == SCM_CCMD_AES_ENC_ECB)) ? "at" : "to",
+ 16 * ((value & SCM_CCMD_OFFSET_MASK) >> SCM_CCMD_OFFSET_SHIFT),
+ (value & SCM_CCMD_PART_MASK) >> SCM_CCMD_PART_SHIFT);
+
+ return print_buffer;
+}
+
+/**
+ * Create a text interpretation of an SCM Access Permissions Register
+ *
+ * @param value The value of the register
+ * @param[out] print_buffer Place to store the interpretation
+ * @param buf_size Number of bytes available at print_buffer
+ *
+ * @return The print_buffer
+ */
+static
+char *scm_print_acc_reg(uint32_t value, char *print_buffer, int buf_size)
+{
+ snprintf(print_buffer, buf_size, "%s%s%s%s%s%s%s%s%s%s",
+ (value & SCM_PERM_NO_ZEROIZE) ? "NO_ZERO " : "",
+ (value & SCM_PERM_HD_SUP_DISABLE) ? "SUP_DIS " : "",
+ (value & SCM_PERM_HD_READ) ? "HD_RD " : "",
+ (value & SCM_PERM_HD_WRITE) ? "HD_WR " : "",
+ (value & SCM_PERM_HD_EXECUTE) ? "HD_EX " : "",
+ (value & SCM_PERM_TH_READ) ? "TH_RD " : "",
+ (value & SCM_PERM_TH_WRITE) ? "TH_WR " : "",
+ (value & SCM_PERM_OT_READ) ? "OT_RD " : "",
+ (value & SCM_PERM_OT_WRITE) ? "OT_WR " : "",
+ (value & SCM_PERM_OT_EXECUTE) ? "OT_EX" : "");
+
+ return print_buffer;
+}
+
+/**
+ * Create a text interpretation of the SCM Partitions Engaged Register
+ *
+ * @param value The value of the register
+ * @param[out] print_buffer Place to store the interpretation
+ * @param buf_size Number of bytes available at print_buffer
+ *
+ * @return The print_buffer
+ */
+static
+char *scm_print_part_eng_reg(uint32_t value, char *print_buffer, int buf_size)
+{
+ snprintf(print_buffer, buf_size, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ (value & 0x8000) ? "15 " : "",
+ (value & 0x4000) ? "14 " : "",
+ (value & 0x2000) ? "13 " : "",
+ (value & 0x1000) ? "12 " : "",
+ (value & 0x0800) ? "11 " : "",
+ (value & 0x0400) ? "10 " : "",
+ (value & 0x0200) ? "9 " : "",
+ (value & 0x0100) ? "8 " : "",
+ (value & 0x0080) ? "7 " : "",
+ (value & 0x0040) ? "6 " : "",
+ (value & 0x0020) ? "5 " : "",
+ (value & 0x0010) ? "4 " : "",
+ (value & 0x0008) ? "3 " : "",
+ (value & 0x0004) ? "2 " : "",
+ (value & 0x0002) ? "1 " : "", (value & 0x0001) ? "0" : "");
+
+ return print_buffer;
+}
+
+/**
+ * Create a text interpretation of the SMN Status Register
+ *
+ * @param value The value of the register
+ * @param[out] print_buffer Place to store the interpretation
+ * @param buf_size Number of bytes available at print_buffer
+ *
+ * @return The print_buffer
+ */
+static
+char *smn_print_status_reg(uint32_t value, char *print_buffer, int buf_size)
+{
+ snprintf(print_buffer, buf_size,
+ "Version %d %s%s%s%s%s%s%s%s%s%s%s%s%s",
+ (value & SMN_STATUS_VERSION_ID_MASK) >>
+ SMN_STATUS_VERSION_ID_SHIFT,
+ (value & SMN_STATUS_ILLEGAL_MASTER) ? "IllMaster " : "",
+ (value & SMN_STATUS_SCAN_EXIT) ? "ScanExit " : "",
+ (value & SMN_STATUS_PERIP_INIT) ? "PeripInit " : "",
+ (value & SMN_STATUS_SMN_ERROR) ? "SMNError " : "",
+ (value & SMN_STATUS_SOFTWARE_ALARM) ? "SWAlarm " : "",
+ (value & SMN_STATUS_TIMER_ERROR) ? "TimerErr " : "",
+ (value & SMN_STATUS_PC_ERROR) ? "PTCTErr " : "",
+ (value & SMN_STATUS_BITBANK_ERROR) ? "BitbankErr " : "",
+ (value & SMN_STATUS_ASC_ERROR) ? "ASCErr " : "",
+ (value & SMN_STATUS_SECURITY_POLICY_ERROR) ? "SecPlcyErr " :
+ "",
+ (value & SMN_STATUS_SEC_VIO_ACTIVE_ERROR) ? "SecVioAct " : "",
+ (value & SMN_STATUS_INTERNAL_BOOT) ? "IntBoot " : "",
+ smn_state_name[(value & SMN_STATUS_STATE_MASK) >>
+ SMN_STATUS_STATE_SHIFT]);
+
+ return print_buffer;
+}
+
+/**
+ * The array, indexed by register number (byte-offset / 4), of print routines
+ * for the SCC (SCM and SMN) registers.
+ */
+static reg_print_routine_t reg_printers[] = {
+ scm_print_version_reg,
+ NULL, /* 0x04 */
+ NULL, /* SCM_INT_CTL_REG */
+ scm_print_status_reg,
+ scm_print_err_status_reg,
+ NULL, /* SCM_FAULT_ADR_REG */
+ NULL, /* SCM_PART_OWNERS_REG */
+ scm_print_part_eng_reg,
+ NULL, /* SCM_UNIQUE_ID0_REG */
+ NULL, /* SCM_UNIQUE_ID1_REG */
+ NULL, /* SCM_UNIQUE_ID2_REG */
+ NULL, /* SCM_UNIQUE_ID3_REG */
+ NULL, /* 0x30 */
+ NULL, /* 0x34 */
+ NULL, /* 0x38 */
+ NULL, /* 0x3C */
+ NULL, /* 0x40 */
+ NULL, /* 0x44 */
+ NULL, /* 0x48 */
+ NULL, /* 0x4C */
+ scm_print_zcmd_reg,
+ scm_print_ccmd_reg,
+ NULL, /* SCM_C_BLACK_ST_REG */
+ NULL, /* SCM_DBG_STATUS_REG */
+ NULL, /* SCM_AES_CBC_IV0_REG */
+ NULL, /* SCM_AES_CBC_IV1_REG */
+ NULL, /* SCM_AES_CBC_IV2_REG */
+ NULL, /* SCM_AES_CBC_IV3_REG */
+ NULL, /* 0x70 */
+ NULL, /* 0x74 */
+ NULL, /* 0x78 */
+ NULL, /* 0x7C */
+ NULL, /* SCM_SMID0_REG */
+ scm_print_acc_reg, /* ACC0 */
+ NULL, /* SCM_SMID1_REG */
+ scm_print_acc_reg, /* ACC1 */
+ NULL, /* SCM_SMID2_REG */
+ scm_print_acc_reg, /* ACC2 */
+ NULL, /* SCM_SMID3_REG */
+ scm_print_acc_reg, /* ACC3 */
+ NULL, /* SCM_SMID4_REG */
+ scm_print_acc_reg, /* ACC4 */
+ NULL, /* SCM_SMID5_REG */
+ scm_print_acc_reg, /* ACC5 */
+ NULL, /* SCM_SMID6_REG */
+ scm_print_acc_reg, /* ACC6 */
+ NULL, /* SCM_SMID7_REG */
+ scm_print_acc_reg, /* ACC7 */
+ NULL, /* SCM_SMID8_REG */
+ scm_print_acc_reg, /* ACC8 */
+ NULL, /* SCM_SMID9_REG */
+ scm_print_acc_reg, /* ACC9 */
+ NULL, /* SCM_SMID10_REG */
+ scm_print_acc_reg, /* ACC10 */
+ NULL, /* SCM_SMID11_REG */
+ scm_print_acc_reg, /* ACC11 */
+ NULL, /* SCM_SMID12_REG */
+ scm_print_acc_reg, /* ACC12 */
+ NULL, /* SCM_SMID13_REG */
+ scm_print_acc_reg, /* ACC13 */
+ NULL, /* SCM_SMID14_REG */
+ scm_print_acc_reg, /* ACC14 */
+ NULL, /* SCM_SMID15_REG */
+ scm_print_acc_reg, /* ACC15 */
+ smn_print_status_reg,
+ NULL, /* SMN_COMMAND_REG */
+ NULL, /* SMN_SEQ_START_REG */
+ NULL, /* SMN_SEQ_END_REG */
+ NULL, /* SMN_SEQ_CHECK_REG */
+ NULL, /* SMN_BB_CNT_REG */
+ NULL, /* SMN_BB_INC_REG */
+ NULL, /* SMN_BB_DEC_REG */
+ NULL, /* SMN_COMPARE_REG */
+ NULL, /* SMN_PT_CHK_REG */
+ NULL, /* SMN_CT_CHK_REG */
+ NULL, /* SMN_TIMER_IV_REG */
+ NULL, /* SMN_TIMER_CTL_REG */
+ NULL, /* SMN_SEC_VIO_REG */
+ NULL, /* SMN_TIMER_REG */
+ NULL, /* SMN_HAC_REG */
+};
+
+/*****************************************************************************/
+/* fn dbg_scc_read_register() */
+/*****************************************************************************/
+/**
+ * Noisily read a 32-bit value to an SCC register.
+ * @param offset The address of the register to read.
+ *
+ * @return The register value
+ * */
+uint32_t dbg_scc_read_register(uint32_t offset)
+{
+ uint32_t value;
+ char *regname = scc_regnames[offset / 4];
+
+ value = __raw_readl(scc_base + offset);
+ pr_debug("SCC2 RD: 0x%03x : 0x%08x (%s) %s\n", offset, value, regname,
+ reg_printers[offset / 4]
+ ? reg_printers[offset / 4] (value, reg_print_buffer,
+ REG_PRINT_BUFFER_SIZE)
+ : "");
+
+ return value;
+}
+
+/*****************************************************************************/
+/* fn dbg_scc_write_register() */
+/*****************************************************************************/
+/*
+ * Noisily read a 32-bit value to an SCC register.
+ * @param offset The address of the register to written.
+ *
+ * @param value The new register value
+ */
+void dbg_scc_write_register(uint32_t offset, uint32_t value)
+{
+ char *regname = scc_regnames[offset / 4];
+
+ pr_debug("SCC2 WR: 0x%03x : 0x%08x (%s) %s\n", offset, value, regname,
+ reg_printers[offset / 4]
+ ? reg_printers[offset / 4] (value, reg_print_buffer,
+ REG_PRINT_BUFFER_SIZE)
+ : "");
+ (void)__raw_writel(value, scc_base + offset);
+
+}
+
+#endif /* SCC_REGISTER_DEBUG */
+
+static int scc_dev_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ int ret = 0;
+
+ /* get the scc registers base address */
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "can't get IORESOURCE_MEM (0)\n");
+ ret = -ENXIO;
+ goto exit;
+ }
+
+ scc_phys_base = r->start;
+
+
+ /* get the scc ram base address */
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!r) {
+ dev_err(&pdev->dev, "can't get IORESOURCE_MEM (1)\n");
+ ret = -ENXIO;
+ goto exit;
+ }
+
+ scm_ram_phys_base = r->start;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18))
+ scc_clk = clk_get(&pdev->dev, "scc_clk");
+#endif
+
+ /* now initialize the SCC */
+ ret = scc_init();
+
+exit:
+ return ret;
+}
+
+static int scc_dev_remove(struct platform_device *pdev)
+{
+ scc_cleanup();
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int scc_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ return 0;
+}
+
+static int scc_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define scc_suspend NULL
+#define scc_resume NULL
+#endif
+
+/*! Linux Driver definition
+ *
+ */
+static struct platform_driver mxcscc_driver = {
+ .driver = {
+ .name = SCC_DRIVER_NAME,
+ },
+ .probe = scc_dev_probe,
+ .remove = scc_dev_remove,
+ .suspend = scc_suspend,
+ .resume = scc_resume,
+};
+
+static int __init scc_driver_init(void)
+{
+ return platform_driver_register(&mxcscc_driver);
+}
+
+module_init(scc_driver_init);
+
+static void __exit scc_driver_exit(void)
+{
+ platform_driver_unregister(&mxcscc_driver);
+}
+
+module_exit(scc_driver_exit);
diff --git a/drivers/mxc/security/scc2_internals.h b/drivers/mxc/security/scc2_internals.h
new file mode 100644
index 000000000000..1c047de0082e
--- /dev/null
+++ b/drivers/mxc/security/scc2_internals.h
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#ifndef SCC_INTERNALS_H
+#define SCC_INTERNALS_H
+
+/** @file scc2_internals.h
+ *
+ * @brief This is intended to be the file which contains most or all of the
+ * code or changes need to port the driver. It also includes other definitions
+ * needed by the driver.
+ *
+ * This header file should only ever be included by scc2_driver.c
+ *
+ * Compile-time flags minimally needed:
+ *
+ * @li Some sort of platform flag. Currently TAHITI and MXC are understood.
+ * @li Some start-of-SCC consideration, such as SCC_BASE_ADDR
+ *
+ * Some changes which could be made when porting this driver:
+ * #SCC_SPIN_COUNT
+ *
+ */
+
+#include <linux/version.h> /* Current version Linux kernel */
+#include <linux/module.h> /* Basic support for loadable modules,
+ printk */
+#include <linux/init.h> /* module_init, module_exit */
+#include <linux/kernel.h> /* General kernel system calls */
+#include <linux/sched.h> /* for interrupt.h */
+#include <linux/spinlock.h>
+
+#include <linux/io.h> /* ioremap() */
+#include <linux/interrupt.h> /* IRQ / interrupt definitions */
+
+
+#include <linux/mxc_scc2_driver.h>
+
+#if defined(MXC)
+
+#include <mach/iim.h>
+#include <mach/mxc_scc.h>
+
+
+/**
+ * This macro is used to determine whether the SCC is enabled/available
+ * on the platform. This macro may need to be ported.
+ */
+#define SCC_FUSE __raw_readl(IO_ADDRESS(IIM_BASE_ADDR + MXC_IIMHWV1))
+#define SCC_ENABLED() ((SCC_FUSE & MXC_IIMHWV1_SCC_DISABLE) == 0)
+
+#else /* neither TAHITI nor MXC */
+
+#error Do not understand target architecture
+
+#endif /* TAHITI */
+/**
+ * Define the number of Stored Keys which the SCC driver will make available.
+ * Value shall be from 0 to 20. Default is zero (0).
+ */
+/*#define SCC_KEY_SLOTS 20*/
+
+
+/* Temporarily define compile-time flags to make Doxygen happy. */
+#ifdef DOXYGEN_HACK
+/** @addtogroup scccompileflags */
+/** @{ */
+
+
+/** @def NO_SMN_INTERRUPT
+ * The SMN interrupt is not wired to the CPU at all.
+ */
+#define NO_SMN_INTERRUPT
+
+
+/**
+ * Register an interrupt handler for the SMN as well as
+ * the SCM. In some implementations, the SMN is not connected at all (see
+ * #NO_SMN_INTERRUPT), and in others, it is on the same interrupt line as the
+ * SCM. When defining this flag, the SMN interrupt should be on a separate
+ * line from the SCM interrupt.
+ */
+
+#define USE_SMN_INTERRUPT
+
+
+/**
+ * Turn on generation of run-time operational, debug, and error messages
+ */
+#define SCC_DEBUG
+
+
+/**
+ * Turn on generation of run-time logging of access to the SCM and SMN
+ * registers.
+ */
+#define SCC_REGISTER_DEBUG
+
+
+/**
+ * Turn on generation of run-time logging of access to the SCM Red and
+ * Black memories. Will only work if #SCC_REGISTER_DEBUG is also defined.
+ */
+#define SCC_RAM_DEBUG
+
+
+/**
+ * If the driver finds the SCC in HEALTH_CHECK state, go ahead and
+ * run a quick ASC to bring it to SECURE state.
+ */
+#define SCC_BRINGUP
+
+
+/**
+ * Expected to come from platform header files or compile command line.
+ * This symbol must be the address of the SCC
+ */
+#define SCC_BASE
+
+/**
+ * This must be the interrupt line number of the SCM interrupt.
+ */
+#define INT_SCM
+
+/**
+ * if #USE_SMN_INTERRUPT is defined, this must be the interrupt line number of
+ * the SMN interrupt.
+ */
+#define INT_SMN
+
+/**
+ * Define the number of Stored Keys which the SCC driver will make available.
+ * Value shall be from 0 to 20. Default is zero (0).
+ */
+#define SCC_KEY_SLOTS
+
+/**
+ * Make sure that this flag is defined if compiling for a Little-Endian
+ * platform. Linux Kernel builds provide this flag.
+ */
+#define __LITTLE_ENDIAN
+
+/**
+ * Make sure that this flag is defined if compiling for a Big-Endian platform.
+ * Linux Kernel builds provide this flag.
+ */
+#define __BIG_ENDIAN
+
+/**
+ * Read a 32-bit register value from a 'peripheral'. Standard Linux/Unix
+ * macro.
+ *
+ * @param offset Bus address of register to be read
+ *
+ * @return The value of the register
+ */
+#define readl(offset)
+
+
+/**
+ * Write a 32-bit value to a register in a 'peripheral'. Standard Linux/Unix
+ * macro.
+ *
+ * @param value The 32-bit value to store
+ * @param offset Bus address of register to be written
+ *
+ * return (none)
+ */
+#define writel(value,offset)
+
+
+/** @} */ /* end group scccompileflags */
+
+#endif /* DOXYGEN_HACK */
+
+
+#ifndef SCC_KEY_SLOTS
+#define SCC_KEY_SLOTS 0
+
+#else
+
+#if (SCC_KEY_SLOTS < 0) || (SCC_KEY_SLOTS > 20)
+#error Bad value for SCC_KEY_SLOTS
+#endif
+
+#endif
+
+
+/**
+ * Maximum length of key/secret value which can be stored in SCC.
+ */
+#define SCC_MAX_KEY_SIZE 256
+
+
+/**
+ * This is the size, in bytes, of each key slot, and therefore the maximum size
+ * of the wrapped key.
+ */
+#define SCC_KEY_SLOT_SIZE 32
+
+
+/* These come for free with Linux, but may need to be set in a port. */
+#ifndef __BIG_ENDIAN
+#ifndef __LITTLE_ENDIAN
+#error One of __LITTLE_ENDIAN or __BIG_ENDIAN must be #defined
+#endif
+#else
+#ifdef __LITTLE_ENDIAN
+#error Exactly one of __LITTLE_ENDIAN or __BIG_ENDIAN must be #defined
+#endif
+#endif
+
+
+#ifndef SCC_CALLBACK_SIZE
+/** The number of function pointers which can be stored in #scc_callbacks.
+ * Defaults to 4, can be overridden with compile-line argument.
+ */
+#define SCC_CALLBACK_SIZE 4
+#endif
+
+
+/** Initial CRC value for CCITT-CRC calculation. */
+#define CRC_CCITT_START 0xFFFF
+
+
+#ifdef TAHITI
+
+/**
+ * The SCC_BASE has to be SMN_BASE_ADDR on TAHITI, as the banks of
+ * registers are swapped in place.
+ */
+#define SCC_BASE SMN_BASE_ADDR
+
+
+/** The interrupt number for the SCC (SCM only!) on Tahiti */
+#define INT_SCC_SCM 62
+
+
+/** Tahiti does not have the SMN interrupt wired to the CPU. */
+#define NO_SMN_INTERRUPT
+
+
+#endif /* TAHITI */
+
+
+/** Number of times to spin between polling of SCC while waiting for cipher
+ * or zeroizing function to complete. See also #SCC_CIPHER_MAX_POLL_COUNT. */
+#define SCC_SPIN_COUNT 1000
+
+
+/** Number of times to polling SCC while waiting for cipher
+ * or zeroizing function to complete. See also #SCC_SPIN_COUNT. */
+#define SCC_CIPHER_MAX_POLL_COUNT 100
+
+
+/**
+ * @def SCC_READ_REGISTER
+ * Read a 32-bit value from an SCC register. Macro which depends upon
+ * #scc_base. Linux readl()/writel() macros operate on 32-bit quantities, as
+ * do SCC register reads/writes.
+ *
+ * @param offset Register offset within SCC.
+ *
+ * @return The value from the SCC's register.
+ */
+#ifndef SCC_REGISTER_DEBUG
+#define SCC_READ_REGISTER(offset) __raw_readl(scc_base+(offset))
+#else
+#define SCC_READ_REGISTER(offset) dbg_scc_read_register(offset)
+#endif
+
+
+/**
+ * Write a 32-bit value to an SCC register. Macro depends upon #scc_base.
+ * Linux readl()/writel() macros operate on 32-bit quantities, as do SCC
+ * register reads/writes.
+ *
+ * @param offset Register offset within SCC.
+ * @param value 32-bit value to store into the register
+ *
+ * @return (void)
+ */
+#ifndef SCC_REGISTER_DEBUG
+#define SCC_WRITE_REGISTER(offset,value) \
+ (void)__raw_writel(value, scc_base+(offset))
+#else
+#define SCC_WRITE_REGISTER(offset,value) \
+ dbg_scc_write_register(offset, value)
+#endif
+
+/**
+ * Calculate the kernel virtual address of a partition from the partition number.
+ */
+#define SCM_PART_ADDRESS(part) \
+ (scm_ram_base + (part*scc_configuration.partition_size_bytes))
+
+/**
+ * Calculate the partition number from the kernel virtual address.
+ */
+#define SCM_PART_NUMBER(address) \
+ ((address - (uint32_t)scm_ram_base)/scc_configuration.partition_size_bytes)
+
+/**
+ * Calculates the byte offset into a word
+ * @param bp The byte (char*) pointer
+ * @return The offset (0, 1, 2, or 3)
+ */
+#define SCC_BYTE_OFFSET(bp) ((uint32_t)(bp) % sizeof(uint32_t))
+
+
+/**
+ * Converts (by rounding down) a byte pointer into a word pointer
+ * @param bp The byte (char*) pointer
+ * @return The word (uint32_t) as though it were an aligned (uint32_t*)
+ */
+#define SCC_WORD_PTR(bp) (((uint32_t)(bp)) & ~(sizeof(uint32_t)-1))
+
+
+/**
+ * Determine number of bytes in an SCC block
+ *
+ * @return Bytes / block
+ */
+#define SCC_BLOCK_SIZE_BYTES() scc_configuration.block_size_bytes
+
+
+/**
+ * Maximum number of additional bytes which may be added in CRC+padding mode.
+ */
+#define PADDING_BUFFER_MAX_BYTES (CRC_SIZE_BYTES + sizeof(scc_block_padding))
+
+/**
+ * Shorthand (clearer, anyway) for number of bytes in a CRC.
+ */
+#define CRC_SIZE_BYTES (sizeof(crc_t))
+
+/**
+ * The polynomial used in CCITT-CRC calculation
+ */
+#define CRC_POLYNOMIAL 0x1021
+
+/**
+ * Calculate CRC on one byte of data
+ *
+ * @param[in,out] running_crc A value of type crc_t where CRC is kept. This
+ * must be an rvalue and an lvalue.
+ * @param[in] byte_value The byte (uint8_t, char) to be put in the CRC
+ *
+ * @return none
+ */
+#define CALC_CRC(byte_value,running_crc) { \
+ uint8_t data; \
+ data = (0xff&(byte_value)) ^ (running_crc >> 8); \
+ running_crc = scc_crc_lookup_table[data] ^ (running_crc << 8); \
+}
+
+/** Value of 'beginning of padding' marker in driver-provided padding */
+#define SCC_DRIVER_PAD_CHAR 0x80
+
+
+/** Name of the driver. Used (on Linux, anyway) when registering interrupts */
+#define SCC_DRIVER_NAME "mxc_scc"
+
+
+/* Port -- these symbols are defined in Linux 2.6 and later. They are defined
+ * here for backwards compatibility because this started life as a 2.4
+ * driver, and as a guide to portation to other platforms.
+ */
+
+#if !defined(LINUX_VERSION_CODE) || LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+
+#define irqreturn_t void /* Return type of an interrupt handler */
+
+#define IRQ_HANDLED /* Would be '1' for handled -- as in return IRQ_HANDLED; */
+
+#define IRQ_NONE /* would be '0' for not handled -- as in return IRQ_NONE; */
+
+#define IRQ_RETVAL(x) /* Return x==0 (not handled) or non-zero (handled) */
+
+#endif /* LINUX earlier than 2.5 */
+
+
+/* These are nice to have around */
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+
+/** Provide a typedef for the CRC which can be used in encrypt/decrypt */
+typedef uint16_t crc_t;
+
+
+/** Gives high-level view of state of the SCC */
+enum scc_status {
+ SCC_STATUS_INITIAL, /**< State of driver before ever checking */
+ SCC_STATUS_CHECKING, /**< Transient state while driver loading */
+ SCC_STATUS_UNIMPLEMENTED, /**< SCC is non-existent or unuseable */
+ SCC_STATUS_OK, /**< SCC is in Secure or Default state */
+ SCC_STATUS_FAILED /**< In Failed state */
+};
+
+/**
+ * Information about a key slot.
+ */
+struct scc_key_slot
+{
+ uint64_t owner_id; /**< Access control value. */
+ uint32_t length; /**< Length of value in slot. */
+ uint32_t offset; /**< Offset of value from start of RAM. */
+ uint32_t status; /**< 0 = unassigned, 1 = assigned. */
+ uint32_t part_ctl; /**< for the CCMD register */
+};
+
+/* Forward-declare a number routines which are not part of user api */
+static int scc_init(void);
+static void scc_cleanup(void);
+static int __init scc_driver_init(void);
+static void __exit scc_driver_exit(void);
+
+/* Forward defines of internal functions */
+OS_DEV_ISR(scc_irq);
+/*static irqreturn_t scc_irq(int irq, void *dev_id);*/
+/** Perform callbacks registered by #scc_monitor_security_failure().
+ *
+ * Make sure callbacks only happen once... Since there may be some reason why
+ * the interrupt isn't generated, this routine could be called from base(task)
+ * level.
+ *
+ * One at a time, go through #scc_callbacks[] and call any non-null pointers.
+ */
+static void scc_perform_callbacks(void);
+/*static uint32_t copy_to_scc(const uint8_t* from, uint32_t to, unsigned long count_bytes, uint16_t* crc);
+static uint32_t copy_from_scc(const uint32_t from, uint8_t* to,unsigned long count_bytes, uint16_t* crc);
+static scc_return_t scc_strip_padding(uint8_t* from,unsigned* count_bytes_stripped);*/
+static uint32_t scc_update_state(void);
+static void scc_init_ccitt_crc(void);
+static uint32_t scc_grab_config_values(void);
+static int setup_interrupt_handling(void);
+/**
+ * Perform an encryption on the input. If @c verify_crc is true, a CRC must be
+ * calculated on the plaintext, and appended, with padding, before computing
+ * the ciphertext.
+ *
+ * @param[in] count_in_bytes Count of bytes of plaintext
+ * @param[in] data_in Pointer to the plaintext
+ * @param[in] scm_control Bit values for the SCM_CONTROL register
+ * @param[in,out] data_out Pointer for storing ciphertext
+ * @param[in] add_crc Flag for computing CRC - 0 no, else yes
+ * @param[in,out] count_out_bytes Number of bytes available at @c data_out
+ */
+/*static scc_return_t scc_encrypt(uint32_t count_in_bytes, uint8_t* data_in, uint32_t scm_control, uint8_t* data_out,int add_crc, unsigned long* count_out_bytes);*/
+/**
+ * Perform a decryption on the input. If @c verify_crc is true, the last block
+ * (maybe the two last blocks) is special - it should contain a CRC and
+ * padding. These must be stripped and verified.
+ *
+ * @param[in] count_in_bytes Count of bytes of ciphertext
+ * @param[in] data_in Pointer to the ciphertext
+ * @param[in] scm_control Bit values for the SCM_CONTROL register
+ * @param[in,out] data_out Pointer for storing plaintext
+ * @param[in] verify_crc Flag for running CRC - 0 no, else yes
+ * @param[in,out] count_out_bytes Number of bytes available at @c data_out
+
+ */
+/*static scc_return_t scc_decrypt(uint32_t count_in_bytes, uint8_t* data_in, uint32_t scm_control, uint8_t* data_out, int verify_crc, unsigned long* count_out_bytes);*/
+static uint32_t host_owns_partition(uint32_t part_no);
+static uint32_t partition_engaged(uint32_t part_no);
+
+static scc_return_t scc_wait_completion(uint32_t* scm_status);
+static int is_cipher_done(uint32_t* scm_status);
+static scc_return_t check_register_accessible (uint32_t offset,
+ uint32_t smn_status,
+ uint32_t scm_status);
+static scc_return_t check_register_offset(uint32_t offset);
+/*uint8_t make_vpu_partition(void);*/
+
+#ifdef SCC_REGISTER_DEBUG
+static uint32_t dbg_scc_read_register(uint32_t offset);
+static void dbg_scc_write_register(uint32_t offset, uint32_t value);
+#endif
+
+
+/* For Linux kernel, export the API functions to other kernel modules */
+EXPORT_SYMBOL(scc_get_configuration);
+EXPORT_SYMBOL(scc_zeroize_memories);
+/*EXPORT_SYMBOL(scc_crypt);*/
+EXPORT_SYMBOL(scc_set_sw_alarm);
+EXPORT_SYMBOL(scc_monitor_security_failure);
+EXPORT_SYMBOL(scc_stop_monitoring_security_failure);
+EXPORT_SYMBOL(scc_read_register);
+EXPORT_SYMBOL(scc_write_register);
+EXPORT_SYMBOL(scc_allocate_partition);
+EXPORT_SYMBOL(scc_engage_partition);
+EXPORT_SYMBOL(scc_release_partition);
+EXPORT_SYMBOL(scc_diminish_permissions);
+EXPORT_SYMBOL(scc_encrypt_region);
+EXPORT_SYMBOL(scc_decrypt_region);
+/*EXPORT_SYMBOL(make_vpu_partition);*/
+
+
+/* Tell Linux this is not GPL code */
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Device Driver for SCC (SMN/SCM)");
+
+
+#endif /* SCC_INTERNALS_H */