[Armadeus-commitlog] armadeus branch, master, updated. armadeus-4.0-2549-g0cd1717
Brought to you by:
sszy
|
From: Julien B a. A. <ar...@us...> - 2011-10-20 11:11:33
|
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "armadeus".
The branch, master has been updated
via 0cd171766a17ed7a8e11083223414d57225084d6 (commit)
via 44e7c62a681e87a29a48c84b2a611b55834f1ef1 (commit)
via 81a11c9925cfac662c04b3bd5c51a2594b742565 (commit)
from 36116f62d98bb248e435349387498ee3c704f408 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 0cd171766a17ed7a8e11083223414d57225084d6
Merge: 44e7c62 36116f6
Author: Julien Boibessot <jul...@ar...>
Date: Thu Oct 20 13:09:58 2011 +0200
Merge branch 'master' of ssh://armadeus.git.sourceforge.net/gitroot/armadeus/armadeus
commit 44e7c62a681e87a29a48c84b2a611b55834f1ef1
Author: Julien Boibessot <jul...@ar...>
Date: Thu Oct 20 13:09:18 2011 +0200
[LINUX] Add SCC and SAHARA drivers (from Freescale BSP)
commit 81a11c9925cfac662c04b3bd5c51a2594b742565
Author: Julien Boibessot <jul...@ar...>
Date: Thu Oct 20 11:35:28 2011 +0200
[BUILDROOT] Add Freescale's imx-lib & imx-test packages (not selectable in menu yet)
-----------------------------------------------------------------------
Summary of changes:
.../package/armadeus/freescale-tools/imx-lib.mk | 43 +
.../package/armadeus/freescale-tools/imx-test.mk | 42 +
...0-add_scc2_and_sahara_drivers_without_rng.patch |31842 ++++++++++++++++++++
3 files changed, 31927 insertions(+), 0 deletions(-)
create mode 100644 buildroot/package/armadeus/freescale-tools/imx-lib.mk
create mode 100644 buildroot/package/armadeus/freescale-tools/imx-test.mk
create mode 100644 patches/linux/2.6.38/442-freescale-0197-ENGR00131650-add_scc2_and_sahara_drivers_without_rng.patch
diff --git a/buildroot/package/armadeus/freescale-tools/imx-lib.mk b/buildroot/package/armadeus/freescale-tools/imx-lib.mk
new file mode 100644
index 0000000..b68be67
--- /dev/null
+++ b/buildroot/package/armadeus/freescale-tools/imx-lib.mk
@@ -0,0 +1,43 @@
+#############################################################
+#
+# imx-lib (helpers librairies from Freescale BSP)
+#
+#############################################################
+IMX_LIB_VERSION = 10.11.01
+IMX_LIB_SOURCE = imx-lib-$(IMX_LIB_VERSION).tar.gz
+IMX_LIB_SITE = ftp://ftp2.armadeus.com/armadeusw/download/
+IMX_LIB_INSTALL_STAGING = YES
+
+ifeq ($(BR2_CPU_NAME),imx27)
+BUILD_PLATFORM = IMX27
+else
+BUILD_PLATFORM = IMX51
+endif
+
+define IMX_LIB_BUILD_CMDS
+ $(MAKE) CC="$(TARGET_CC)" \
+ PLATFORM=$(BUILD_PLATFORM) \
+ DIRS="sahara2 vpu" \
+ CFLAGS="-fPIC -I$(LINUX26_DIR)/drivers/mxc/security/sahara2/include" \
+ -C $(@D)
+endef
+
+define IMX_LIB_INSTALL_STAGING_CMDS
+ install -D -m 755 $(@D)/vpu/libvpu.so $(STAGING_DIR)/usr/lib
+ install -D -m 755 $(@D)/sahara2/libsahara.so $(STAGING_DIR)/usr/lib
+endef
+
+define IMX_LIB_INSTALL_TARGET_CMDS
+ install -D -m 755 $(@D)/vpu/libvpu.so $(TARGET_DIR)/usr/lib
+ install -D -m 755 $(@D)/sahara2/libsahara.so $(TARGET_DIR)/usr/lib
+endef
+
+define IMX_LIB_UNINSTALL_TARGET_CMDS
+ rm -f $(TARGET_DIR)/usr/lib/libvpu.so
+endef
+
+define IMX_LIB_CLEAN_CMDS
+ -$(MAKE) -C $(@D) clean
+endef
+
+$(eval $(call GENTARGETS,package,imx-lib))
diff --git a/buildroot/package/armadeus/freescale-tools/imx-test.mk b/buildroot/package/armadeus/freescale-tools/imx-test.mk
new file mode 100644
index 0000000..8ecbff9
--- /dev/null
+++ b/buildroot/package/armadeus/freescale-tools/imx-test.mk
@@ -0,0 +1,42 @@
+#############################################################
+#
+# imx-test (test tools from Freescale BSP)
+#
+#############################################################
+IMX_TEST_VERSION = 10.11.01
+IMX_TEST_SOURCE = imx-test-$(IMX_TEST_VERSION).tar.gz
+IMX_TEST_SITE = ftp://ftp2.armadeus.com/armadeusw/download/
+
+ifeq ($(BR2_CPU_NAME),imx27)
+BUILD_PLATFORM = IMX27
+BUILD_CFLAGS = -DCONFIG_ARCH_MX2
+else
+BUILD_PLATFORM = IMX51
+BUILD_CFLAGS = -DCONFIG_ARCH_MX5
+endif
+
+define IMX_TEST_BUILD_CMDS
+ $(MAKE) CC="$(TARGET_CC)" \
+ VERBOSE="" \
+ LINUXPATH=$(LINUX26_DIR) \
+ CROSS_COMPILE="$(HOST_DIR)/usr/bin/arm-linux-" \
+ PLATFORM=$(BUILD_PLATFORM) \
+ DIRS="mxc_sahara_test mxc_scc2 mxc_spi_test mxc_uart_test mxc_usb_test" \
+ CFLAGS="$(BUILD_CFLAGS) -I$(LINUX26_DIR)/drivers/mxc/security/sahara2/include" \
+ -C $(@D)
+endef
+
+define IMX_TEST_INSTALL_TARGET_CMDS
+ $(MAKE) LINUXPATH=$(LINUX26_DIR) INSTALL_MOD_PATH=$(TARGET_DIR) install -C $(@D)/module_test
+ $(MAKE) DESTDIR=$(TARGET_DIR)/unit_tests PLATFORM=IMX51 install -C $(@D)
+endef
+
+define IMX_TEST_UNINSTALL_TARGET_CMDS
+ rm -rf $(TARGET_DIR)/unit_tests
+endef
+
+define IMX_TEST_CLEAN_CMDS
+ -$(MAKE) -C $(@D) clean
+endef
+
+$(eval $(call GENTARGETS,package,imx-test))
diff --git a/patches/linux/2.6.38/442-freescale-0197-ENGR00131650-add_scc2_and_sahara_drivers_without_rng.patch b/patches/linux/2.6.38/442-freescale-0197-ENGR00131650-add_scc2_and_sahara_drivers_without_rng.patch
new file mode 100644
index 0000000..7e68abc
--- /dev/null
+++ b/patches/linux/2.6.38/442-freescale-0197-ENGR00131650-add_scc2_and_sahara_drivers_without_rng.patch
@@ -0,0 +1,31842 @@
+From 20100e633c68787378f6a933c3da266edfe495d4 Mon Sep 17 00:00:00 2001
+From: Anish Trivedi <an...@fr...>
+Date: Fri, 17 Sep 2010 12:11:29 -0500
+Subject: [PATCH] ENGR00131650 SCC2 and SAHARA drivers upgrade to 2.6.35
+
+Add security driver files. SCC2 unit tests all pass on MX51 and
+MX53. Some SAHARA kernel mode unit tests fail on both SOCs
+(see ENGR00131651), however user mode tests pass.
+
+Signed-off-by: Anish Trivedi <an...@fr...>
+---
+ Modifications done by Armadeus systems:
+ - removal of RNG stuff
+ - compatibility with 2.6.38
+
+ drivers/mxc/security/Kconfig | 53 +
+ drivers/mxc/security/Makefile | 10 +
+ drivers/mxc/security/mxc_scc.c | 2386 ++++++++++++++++
+ drivers/mxc/security/mxc_scc_internals.h | 498 ++++
+ drivers/mxc/security/sahara2/Kconfig | 35 +
+ drivers/mxc/security/sahara2/Makefile | 47 +
+ drivers/mxc/security/sahara2/fsl_shw_auth.c | 706 +++++
+ drivers/mxc/security/sahara2/fsl_shw_hash.c | 186 ++
+ drivers/mxc/security/sahara2/fsl_shw_hmac.c | 266 ++
+ drivers/mxc/security/sahara2/fsl_shw_keystore.c | 837 ++++++
+ drivers/mxc/security/sahara2/fsl_shw_rand.c | 96 +
+ drivers/mxc/security/sahara2/fsl_shw_sym.c | 281 ++
+ drivers/mxc/security/sahara2/fsl_shw_user.c | 137 +
+ drivers/mxc/security/sahara2/fsl_shw_wrap.c | 967 +++++++
+ drivers/mxc/security/sahara2/include/adaptor.h | 113 +
+ drivers/mxc/security/sahara2/include/diagnostic.h | 116 +
+ .../mxc/security/sahara2/include/fsl_platform.h | 161 ++
+ drivers/mxc/security/sahara2/include/fsl_shw.h | 2515 +++++++++++++++++
+ .../security/sahara2/include/fsl_shw_keystore.h | 475 ++++
+ drivers/mxc/security/sahara2/include/linux_port.h | 1806 ++++++++++++
+ .../sahara2/include/platform_abstractions.h | 15 +
+ drivers/mxc/security/sahara2/include/portable_os.h | 1453 ++++++++++
+ .../security/sahara2/include/sah_driver_common.h | 102 +
+ .../sahara2/include/sah_hardware_interface.h | 99 +
+ .../sahara2/include/sah_interrupt_handler.h | 42 +
+ drivers/mxc/security/sahara2/include/sah_kernel.h | 113 +
+ .../security/sahara2/include/sah_memory_mapper.h | 79 +
+ .../security/sahara2/include/sah_queue_manager.h | 63 +
+ .../security/sahara2/include/sah_status_manager.h | 228 ++
+ drivers/mxc/security/sahara2/include/sahara.h | 2265 +++++++++++++++
+ .../mxc/security/sahara2/include/sahara2_kernel.h | 49 +
+ drivers/mxc/security/sahara2/include/sf_util.h | 466 +++
+ drivers/mxc/security/sahara2/km_adaptor.c | 849 ++++++
+ .../mxc/security/sahara2/sah_driver_interface.c | 2179 ++++++++++++++
+ .../mxc/security/sahara2/sah_hardware_interface.c | 808 ++++++
+ .../mxc/security/sahara2/sah_interrupt_handler.c | 216 ++
+ drivers/mxc/security/sahara2/sah_memory_mapper.c | 2356 ++++++++++++++++
+ drivers/mxc/security/sahara2/sah_queue.c | 249 ++
+ drivers/mxc/security/sahara2/sah_queue_manager.c | 1050 +++++++
+ drivers/mxc/security/sahara2/sah_status_manager.c | 734 +++++
+ drivers/mxc/security/sahara2/sf_util.c | 1390 +++++++++
+ drivers/mxc/security/scc2_driver.c | 2393 ++++++++++++++++
+ drivers/mxc/security/scc2_internals.h | 519 ++++
+ 67 files changed, 40654 insertions(+), 3 deletions(-)
+
+Index: linux-2.6.38.8/drivers/mxc/security/Kconfig
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.38.8/drivers/mxc/security/Kconfig 2011-08-31 15:28:27.000000000 +0200
+@@ -0,0 +1,56 @@
++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.
++
++if (ARCH_MX37 || ARCH_MX5 || ARCH_MX27)
++source "drivers/mxc/security/sahara2/Kconfig"
++endif
++
++endmenu
+Index: linux-2.6.38.8/drivers/mxc/security/Makefile
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.38.8/drivers/mxc/security/Makefile 2011-08-31 15:28:27.000000000 +0200
+@@ -0,0 +1,10 @@
++# 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/
+Index: linux-2.6.38.8/drivers/mxc/security/mxc_scc.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.38.8/drivers/mxc/security/mxc_scc.c 2011-08-31 15:28:27.000000000 +0200
+@@ -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 ...
[truncated message content] |