summaryrefslogtreecommitdiff
path: root/drivers/crypto/ux500/hash/hash_core.c
diff options
context:
space:
mode:
authorRobert Marklund <robert.marklund@stericsson.com>2010-10-07 20:05:22 +0200
committerUlf Hansson <ulf.hansson@stericsson.com>2011-09-19 15:14:45 +0200
commit684ee860b3c15698a8e9d3a810e0f3944d2c589e (patch)
tree1bbd178c0979839954b5943b10ced60f9d8505f2 /drivers/crypto/ux500/hash/hash_core.c
parent668d0b953d180811b42940e40556731d2c3cfba6 (diff)
HCL driver implementation (hash).
Major refactoring, checkpatch fixes, documentation of functions, structures, enums. Also moved a lot of code from hash_alg.c to hash_core.c. Fixed problem with incorrect digest when doing HMAC calculations. The problem was that the function that did message pad seems to handle padding for keys incorrectly. Now we do not say how many valid bits there are in the last word when it comes to the key and then the hardware seems to handle the key correctly. Fixed contextsaving so hmac(sha1) passes. There was an if statement checking the DINF bit which was never set. I have removed this and then contextsaving for hmac(sha1) is working. Code up to date with the new arch/arm/mach-ux500 folder structure. Signed-off-by: Joakim Bech <joakim.xx.bech@stericsson.com> ux500: switch to DMAENGINE-based DMA driver Switch all DMA clients to the DMA Engine API, and add the platform hooks for the DMA Engine-based DMA40 driver. Signed-off-by: Rabin Vincent <rabin.vincent@stericsson.com> Signed-off-by: Robert Marklund <robert.marklund@stericsson.com> Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/crypto/ux500/hash/hash_core.c')
-rwxr-xr-xdrivers/crypto/ux500/hash/hash_core.c1756
1 files changed, 1756 insertions, 0 deletions
diff --git a/drivers/crypto/ux500/hash/hash_core.c b/drivers/crypto/ux500/hash/hash_core.c
new file mode 100755
index 00000000000..fd5f8a870bf
--- /dev/null
+++ b/drivers/crypto/ux500/hash/hash_core.c
@@ -0,0 +1,1756 @@
+/*
+ * Cryptographic API.
+ *
+ * Support for Nomadik hardware crypto engine.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+
+#include <linux/crypto.h>
+#include <crypto/internal/hash.h>
+#include <crypto/sha.h>
+
+#include <mach/debug.h>
+#include <mach/hardware.h>
+
+#include "hash_alg.h"
+
+#define DRIVER_NAME "DRIVER HASH"
+/* enables/disables debug msgs */
+#define DRIVER_DEBUG 1
+#define DRIVER_DEBUG_PFX DRIVER_NAME
+#define DRIVER_DBG KERN_ERR
+
+#define MAX_HASH_DIGEST_BYTE_SIZE 32
+#define HASH_BLOCK_BYTE_SIZE 64
+
+#define HASH_ACC_SYNC_CONTROL
+#ifdef HASH_ACC_SYNC_CONTROL
+static struct mutex hash_hw_acc_mutex;
+#endif
+
+int debug;
+static int mode;
+static int contextsaving;
+static struct hash_system_context g_sys_ctx;
+
+/**
+ * struct hash_driver_data - IO Base and clock.
+ * @base: The IO base for the block
+ * @clk: FIXME, add comment
+ */
+struct hash_driver_data {
+ void __iomem *base;
+ struct clk *clk;
+};
+
+/**
+ * struct hash_ctx - The context used for hash calculations.
+ * @key: The key used in the operation
+ * @keylen: The length of the key
+ * @updated: Indicates if hardware is initialized for new operations
+ * @state: The state of the current calculations
+ * @config: The current configuration
+ */
+struct hash_ctx {
+ u8 key[HASH_BLOCK_BYTE_SIZE];
+ u32 keylen;
+ u8 updated;
+ struct hash_state state;
+ struct hash_config config;
+};
+
+/**
+ * struct hash_tfm_ctx - Transform context
+ * @key: The key stored in the transform context
+ * @keylen: The length of the key in the transform context
+ */
+struct hash_tfm_ctx {
+ u8 key[HASH_BLOCK_BYTE_SIZE];
+ u32 keylen;
+};
+
+/* Declaration of functions */
+static void hash_messagepad(int hid, const u32 *message, u8 index_bytes);
+
+/**
+ * hexdump - Dumps buffers in hex.
+ * @buf: The buffer to dump
+ * @len: The length of the buffer
+ */
+static void hexdump(unsigned char *buf, unsigned int len)
+{
+ print_hex_dump(KERN_CONT, "", DUMP_PREFIX_OFFSET,
+ 16, 1, buf, len, false);
+}
+
+/**
+ * clear_reg_str - Clear the registry hash_str.
+ * @hid: Hardware device ID
+ *
+ * This function will clear the dcal bit and the nblw bits.
+ */
+static inline void clear_reg_str(int hid)
+{
+ /* We will only clear the valid registers and not the reserved */
+ g_sys_ctx.registry[hid]->str &= ~HASH_STR_DCAL_MASK;
+ g_sys_ctx.registry[hid]->str &= ~HASH_STR_NBLW_MASK;
+}
+
+/**
+ * write_nblw - Writes the number of valid bytes to nblw.
+ * @hid: Hardware device ID
+ * @bytes: The number of valid bytes in last word of a message
+ *
+ * Note that this function only writes, i.e. it does not clear the registry
+ * before it writes the new data.
+ */
+static inline void write_nblw(int hid, int bytes)
+{
+ g_sys_ctx.registry[hid]->str |=
+ ((bytes * 8) & HASH_STR_NBLW_MASK);
+}
+
+/**
+ * write_dcal - Write/set the dcal bit.
+ * @hid: Hardware device ID
+ */
+static inline void write_dcal(int hid)
+{
+ g_sys_ctx.registry[hid]->str |= (1 << HASH_STR_DCAL_POS);
+}
+
+/**
+ * pad_message - Function that pads a message.
+ * @hid: Hardware device ID
+ *
+ * FIXME: This function should be replaced.
+ */
+static inline void pad_message(int hid)
+{
+ hash_messagepad(hid, g_sys_ctx.state[hid].buffer,
+ g_sys_ctx.state[hid].index);
+}
+
+/**
+ * write_key - Writes the key to the hardware registries.
+ * @hid: Hardware device ID
+ * @key: The key used in the operation
+ * @keylen: The length of the key
+ *
+ * Note that in this function we DO NOT write to the NBLW registry even though
+ * the hardware reference manual says so. There must be incorrect information in
+ * the manual or there must be a bug in the state machine in the hardware.
+ */
+static void write_key(int hid, const u8 *key, u32 keylen)
+{
+ u32 word = 0;
+ clear_reg_str(hid);
+
+ while (keylen >= 4) {
+ word = ((u32) (key[3] & 255) << 24) |
+ ((u32) (key[2] & 255) << 16) |
+ ((u32) (key[1] & 255) << 8) |
+ ((u32) (key[0] & 255));
+
+ HASH_SET_DIN(word);
+ keylen -= 4;
+ key += 4;
+ }
+
+ /* This takes care of the remaining bytes on the last word */
+ if (keylen) {
+ word = 0;
+ while (keylen) {
+ word |= (key[keylen - 1] << (8 * (keylen - 1)));
+ keylen--;
+ }
+ HASH_SET_DIN(word);
+ }
+
+ write_dcal(hid);
+}
+
+/**
+ * init_hash_hw - Initialise the hash hardware for a new calculation.
+ * @desc: The hash descriptor for the job
+ *
+ * This function will enable the bits needed to clear and start a new
+ * calculation.
+ */
+static int init_hash_hw(struct shash_desc *desc)
+{
+ int ret = 0;
+ int hash_error = HASH_OK;
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ stm_dbg(debug, "[init_hash_hw] (ctx=0x%x)!", (u32)ctx);
+
+ hash_error = hash_setconfiguration(HASH_DEVICE_ID_1, &ctx->config);
+ if (hash_error != HASH_OK) {
+ stm_error("hash_setconfiguration() failed!");
+ ret = -1;
+ goto out;
+ }
+
+ hash_error = hash_begin(HASH_DEVICE_ID_1);
+ if (hash_error != HASH_OK) {
+ stm_error("hash_begin() failed!");
+ ret = -1;
+ goto out;
+ }
+
+ if (ctx->config.oper_mode == HASH_OPER_MODE_HMAC) {
+ stm_dbg(debug, "[init_hash_hw] update key=0x%0x, len=%d",
+ (u32) ctx->key, ctx->keylen);
+ write_key(HASH_DEVICE_ID_1, ctx->key, ctx->keylen);
+ }
+
+out:
+ return ret;
+}
+
+/**
+ * hash_init - Common hash init function for SHA1/SHA2 (SHA256).
+ * @desc: The hash descriptor for the job
+ *
+ * Initialize structures and copy the key from the transform context to the
+ * descriptor context if the mode is HMAC.
+ */
+static int hash_init(struct shash_desc *desc)
+{
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+ struct hash_tfm_ctx *tfm_ctx = crypto_tfm_ctx(&desc->tfm->base);
+
+ stm_dbg(debug, "[hash_init]: (ctx=0x%x)!", (u32)ctx);
+
+ if (ctx->config.oper_mode == HASH_OPER_MODE_HMAC) {
+ if (tfm_ctx->key) {
+ memcpy(ctx->key, tfm_ctx->key, tfm_ctx->keylen);
+ ctx->keylen = tfm_ctx->keylen;
+ }
+ }
+
+ memset(&ctx->state, 0, sizeof(struct hash_state));
+ ctx->updated = 0;
+
+ return 0;
+}
+
+/**
+ * hash_update - The hash update function for SHA1/SHA2 (SHA256).
+ * @desc: The hash descriptor for the job
+ * @data: Message that should be hashed
+ * @len: The length of the message that should be hashed
+ */
+static int hash_update(struct shash_desc *desc, const u8 *data,
+ unsigned int len)
+{
+ int ret = 0;
+ int hash_error = HASH_OK;
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ stm_dbg(debug, "[hash_update]: (ctx=0x%x, data=0x%x, len=%d)!",
+ (u32)ctx, (u32)data, len);
+
+#ifdef HASH_ACC_SYNC_CONTROL
+ mutex_lock(&hash_hw_acc_mutex);
+#endif
+
+ if (!ctx->updated) {
+ ret = init_hash_hw(desc);
+ if (ret) {
+ stm_error("init_hash_hw() failed!");
+ goto out;
+ }
+ }
+
+ if (contextsaving) {
+ if (ctx->updated) {
+ hash_error =
+ hash_resume_state(HASH_DEVICE_ID_1, &ctx->state);
+ if (hash_error != HASH_OK) {
+ stm_error("hash_resume_state() failed!");
+ ret = -1;
+ goto out;
+ }
+ }
+ }
+
+ /* NOTE: The length of the message is in the form of number of bits */
+ hash_error = hash_hw_update(HASH_DEVICE_ID_1, data, len * 8);
+ if (hash_error != HASH_OK) {
+ stm_error("hash_hw_update() failed!");
+ ret = -1;
+ goto out;
+ }
+
+ if (contextsaving) {
+ hash_error =
+ hash_save_state(HASH_DEVICE_ID_1, &ctx->state);
+ if (hash_error != HASH_OK) {
+ stm_error("hash_save_state() failed!");
+ ret = -1;
+ goto out;
+ }
+
+ }
+ ctx->updated = 1;
+
+out:
+#ifdef HASH_ACC_SYNC_CONTROL
+ mutex_unlock(&hash_hw_acc_mutex);
+#endif
+ return ret;
+}
+
+/**
+ * hash_final - The hash final function for SHA1/SHA2 (SHA256).
+ * @desc: The hash descriptor for the job
+ * @out: Pointer for the calculated digest
+ */
+static int hash_final(struct shash_desc *desc, u8 *out)
+{
+ int ret = 0;
+ int hash_error = HASH_OK;
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ int digestsize = crypto_shash_digestsize(desc->tfm);
+ u8 digest[HASH_MSG_DIGEST_SIZE];
+
+ stm_dbg(debug, "[hash_final]: (ctx=0x%x)!", (u32) ctx);
+
+#ifdef HASH_ACC_SYNC_CONTROL
+ mutex_lock(&hash_hw_acc_mutex);
+#endif
+
+ if (contextsaving) {
+ hash_error = hash_resume_state(HASH_DEVICE_ID_1, &ctx->state);
+
+ if (hash_error != HASH_OK) {
+ stm_error("hash_resume_state() failed!");
+ ret = -1;
+ goto out;
+ }
+ }
+
+ pad_message(HASH_DEVICE_ID_1);
+
+ if (ctx->config.oper_mode == HASH_OPER_MODE_HMAC)
+ write_key(HASH_DEVICE_ID_1, ctx->key, ctx->keylen);
+
+ hash_error = hash_get_digest(HASH_DEVICE_ID_1, digest);
+
+ memcpy(out, digest, digestsize);
+
+out:
+#ifdef HASH_ACC_SYNC_CONTROL
+ mutex_unlock(&hash_hw_acc_mutex);
+#endif
+
+ return ret;
+}
+
+/**
+ * hash_setkey - The setkey function for providing the key during HMAC
+ * calculations.
+ * @tfm: Pointer to the transform
+ * @key: The key used in the operation
+ * @keylen: The length of the key
+ * @alg: The algorithm to use in the operation
+ */
+static int hash_setkey(struct crypto_shash *tfm, const u8 *key,
+ unsigned int keylen, int alg)
+{
+ int ret = 0;
+ int hash_error = HASH_OK;
+
+ struct hash_tfm_ctx *ctx_tfm = crypto_shash_ctx(tfm);
+
+ stm_dbg(debug, "[hash_setkey]: (ctx_tfm=0x%x, key=0x%x, keylen=%d)!",
+ (u32) ctx_tfm, (u32) key, keylen);
+
+ /* Truncate the key to block size */
+ if (keylen > HASH_BLOCK_BYTE_SIZE) {
+ struct hash_config config;
+ u8 digest[MAX_HASH_DIGEST_BYTE_SIZE];
+ unsigned int digestsize = crypto_shash_digestsize(tfm);
+
+ config.algorithm = alg;
+ config.data_format = HASH_DATA_8_BITS;
+ config.oper_mode = HASH_OPER_MODE_HASH;
+
+#ifdef HASH_ACC_SYNC_CONTROL
+ mutex_lock(&hash_hw_acc_mutex);
+#endif
+ hash_error = hash_compute(HASH_DEVICE_ID_1, key, keylen * 8,
+ &config, digest);
+#ifdef HASH_ACC_SYNC_CONTROL
+ mutex_unlock(&hash_hw_acc_mutex);
+#endif
+ if (hash_error != HASH_OK) {
+ stm_error("Error: hash_compute() failed!");
+ ret = -1;
+ goto out;
+ }
+
+ memcpy(ctx_tfm->key, digest, digestsize);
+ ctx_tfm->keylen = digestsize;
+ } else {
+ memcpy(ctx_tfm->key, key, keylen);
+ ctx_tfm->keylen = keylen;
+ }
+
+out:
+ return ret;
+}
+
+/**
+ * sha1_init - SHA1 init function.
+ * @desc: The hash descriptor for the job
+ */
+static int sha1_init(struct shash_desc *desc)
+{
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ stm_dbg(debug, "[sha1_init]: (ctx=0x%x)!", (u32) ctx);
+
+ ctx->config.data_format = HASH_DATA_8_BITS;
+ ctx->config.algorithm = HASH_ALGO_SHA1;
+ ctx->config.oper_mode = HASH_OPER_MODE_HASH;
+
+ return hash_init(desc);
+}
+
+/**
+ * sha256_init - SHA2 (SHA256) init function.
+ * @desc: The hash descriptor for the job
+ */
+static int sha256_init(struct shash_desc *desc)
+{
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ stm_dbg(debug, "[sha256_init]: (ctx=0x%x)!", (u32) ctx);
+
+ ctx->config.data_format = HASH_DATA_8_BITS;
+ ctx->config.algorithm = HASH_ALGO_SHA2;
+ ctx->config.oper_mode = HASH_OPER_MODE_HASH;
+
+ return hash_init(desc);
+}
+
+/**
+ * hmac_sha1_init - SHA1 HMAC init function.
+ * @desc: The hash descriptor for the job
+ */
+static int hmac_sha1_init(struct shash_desc *desc)
+{
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ stm_dbg(debug, "[hmac_sha1_init]: (ctx=0x%x)!", (u32) ctx);
+
+ ctx->config.data_format = HASH_DATA_8_BITS;
+ ctx->config.algorithm = HASH_ALGO_SHA1;
+ ctx->config.oper_mode = HASH_OPER_MODE_HMAC;
+ ctx->config.hmac_key = HASH_SHORT_KEY;
+
+ return hash_init(desc);
+}
+
+/**
+ * hmac_sha256_init - SHA2 (SHA256) HMAC init function.
+ * @desc: The hash descriptor for the job
+ */
+static int hmac_sha256_init(struct shash_desc *desc)
+{
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ stm_dbg(debug, "[hmac_sha256_init]: (ctx=0x%x)!", (u32) ctx);
+
+ ctx->config.data_format = HASH_DATA_8_BITS;
+ ctx->config.algorithm = HASH_ALGO_SHA2;
+ ctx->config.oper_mode = HASH_OPER_MODE_HMAC;
+ ctx->config.hmac_key = HASH_SHORT_KEY;
+
+ return hash_init(desc);
+}
+
+/**
+ * hmac_sha1_setkey - SHA1 HMAC setkey function.
+ * @tfm: Pointer to the transform
+ * @key: The key used in the operation
+ * @keylen: The length of the key
+ */
+static int hmac_sha1_setkey(struct crypto_shash *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ stm_dbg(debug, "[hmac_sha1_setkey]: (tfm=0x%x, key=0x%x, keylen=%d)!",
+ (u32) tfm, (u32) key, keylen);
+
+ return hash_setkey(tfm, key, keylen, HASH_ALGO_SHA1);
+}
+
+/**
+ * hmac_sha256_setkey - SHA2 (SHA256) HMAC setkey function.
+ * @tfm: Pointer to the transform
+ * @key: The key used in the operation
+ * @keylen: The length of the key
+ */
+static int hmac_sha256_setkey(struct crypto_shash *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ stm_dbg(debug, "[hmac_sha256_setkey]: (tfm=0x%x, key=0x%x, keylen=%d)!",
+ (u32) tfm, (u32) key, keylen);
+
+ return hash_setkey(tfm, key, keylen, HASH_ALGO_SHA2);
+}
+
+static struct shash_alg sha1_alg = {
+ .digestsize = SHA1_DIGEST_SIZE,
+ .init = sha1_init,
+ .update = hash_update,
+ .final = hash_final,
+ .descsize = sizeof(struct hash_ctx),
+ .base = {
+ .cra_name = "sha1",
+ .cra_driver_name = "sha1-u8500",
+ .cra_flags = CRYPTO_ALG_TYPE_DIGEST |
+ CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA1_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct hash_tfm_ctx),
+ .cra_module = THIS_MODULE,
+ }
+};
+
+static struct shash_alg sha256_alg = {
+ .digestsize = SHA256_DIGEST_SIZE,
+ .init = sha256_init,
+ .update = hash_update,
+ .final = hash_final,
+ .descsize = sizeof(struct hash_ctx),
+ .base = {
+ .cra_name = "sha256",
+ .cra_driver_name = "sha256-u8500",
+ .cra_flags = CRYPTO_ALG_TYPE_DIGEST |
+ CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA256_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct hash_tfm_ctx),
+ .cra_module = THIS_MODULE,
+ }
+};
+
+static struct shash_alg hmac_sha1_alg = {
+ .digestsize = SHA1_DIGEST_SIZE,
+ .init = hmac_sha1_init,
+ .update = hash_update,
+ .final = hash_final,
+ .setkey = hmac_sha1_setkey,
+ .descsize = sizeof(struct hash_ctx),
+ .base = {
+ .cra_name = "hmac(sha1)",
+ .cra_driver_name = "hmac(sha1-u8500)",
+ .cra_flags = CRYPTO_ALG_TYPE_DIGEST |
+ CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA1_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct hash_tfm_ctx),
+ .cra_module = THIS_MODULE,
+ }
+};
+
+static struct shash_alg hmac_sha256_alg = {
+ .digestsize = SHA256_DIGEST_SIZE,
+ .init = hmac_sha256_init,
+ .update = hash_update,
+ .final = hash_final,
+ .setkey = hmac_sha256_setkey,
+ .descsize = sizeof(struct hash_ctx),
+ .base = {
+ .cra_name = "hmac(sha256)",
+ .cra_driver_name = "hmac(sha256-u8500)",
+ .cra_flags = CRYPTO_ALG_TYPE_DIGEST |
+ CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA256_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct hash_tfm_ctx),
+ .cra_module = THIS_MODULE,
+ }
+};
+
+/**
+ * u8500_hash_probe - Function that probes the hash hardware.
+ * @pdev: The platform device
+ */
+static int u8500_hash_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ int hash_error = HASH_OK;
+ struct resource *res = NULL;
+ struct hash_driver_data *hash_drv_data;
+
+ stm_dbg(debug, "[u8500_hash_probe]: (pdev=0x%x)", (u32) pdev);
+
+ stm_dbg(debug, "[u8500_hash_probe]: Calling kzalloc()!");
+ hash_drv_data = kzalloc(sizeof(struct hash_driver_data), GFP_KERNEL);
+ if (!hash_drv_data) {
+ stm_dbg(debug, "kzalloc() failed!");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ stm_dbg(debug, "[u8500_hash_probe]: Calling platform_get_resource()!");
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ stm_dbg(debug, "platform_get_resource() failed");
+ ret = -ENODEV;
+ goto out_kfree;
+ }
+
+ stm_dbg(debug, "[u8500_hash_probe]: Calling request_mem_region()!");
+ res = request_mem_region(res->start, res->end - res->start + 1,
+ pdev->name);
+ if (res == NULL) {
+ stm_dbg(debug, "request_mem_region() failed");
+ ret = -EBUSY;
+ goto out_kfree;
+ }
+
+ stm_dbg(debug, "[u8500_hash_probe]: Calling ioremap()!");
+ hash_drv_data->base = ioremap(res->start, res->end - res->start + 1);
+ if (!hash_drv_data->base) {
+ stm_error
+ ("[u8500_hash] ioremap of hash1 register memory failed!");
+ ret = -ENOMEM;
+ goto out_free_mem;
+ }
+
+ stm_dbg(debug, "[u8500_hash_probe]: Calling clk_get()!");
+ /* Enable the clk for HASH1 hardware block */
+ hash_drv_data->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(hash_drv_data->clk)) {
+ stm_error("clk_get() failed!");
+ ret = PTR_ERR(hash_drv_data->clk);
+ goto out_unmap;
+ }
+
+ stm_dbg(debug, "[u8500_hash_probe]: Calling clk_enable()!");
+ ret = clk_enable(hash_drv_data->clk);
+ if (ret) {
+ stm_error("clk_enable() failed!");
+ goto out_unmap;
+ }
+
+ stm_dbg(debug,
+ "[u8500_hash_probe]: Calling hash_init_base_address()->"
+ "(base=0x%x,DEVICE_ID=%d)!",
+ (u32) hash_drv_data->base, HASH_DEVICE_ID_1);
+
+ /* Setting base address */
+ hash_error =
+ hash_init_base_address(HASH_DEVICE_ID_1,
+ (t_logical_address) hash_drv_data->base);
+ if (hash_error != HASH_OK) {
+ stm_error("hash_init_base_address() failed!");
+ ret = -1; /*TODO: what error code should be used here!? */
+ goto out_clk;
+ }
+#ifdef HASH_ACC_SYNC_CONTROL
+ stm_dbg(debug, "[u8500_hash_probe]: Calling mutex_init()!");
+ mutex_init(&hash_hw_acc_mutex);
+#endif
+
+ if (mode == 0) {
+ stm_dbg(debug,
+ "[u8500_hash_probe]: To register all algorithms!");
+
+ ret = crypto_register_shash(&sha1_alg);
+ if (ret) {
+ stm_error("Could not register sha1_alg!");
+ goto out_clk;
+ }
+ stm_dbg(debug, "[u8500_hash_probe]: sha1_alg registered!");
+
+ ret = crypto_register_shash(&sha256_alg);
+ if (ret) {
+ stm_error("Could not register sha256_alg!");
+ goto out_unreg1;
+ }
+ stm_dbg(debug, "[u8500_hash_probe]: sha256_alg registered!");
+
+ ret = crypto_register_shash(&hmac_sha1_alg);
+ if (ret) {
+ stm_error("Could not register hmac_sha1_alg!");
+ goto out_unreg2;
+ }
+ stm_dbg(debug, "[u8500_hash_probe]: hmac_sha1_alg registered!");
+
+ ret = crypto_register_shash(&hmac_sha256_alg);
+ if (ret) {
+ stm_error("Could not register hmac_sha256_alg!");
+ goto out_unreg3;
+ }
+ stm_dbg(debug,
+ "[u8500_hash_probe]: hmac_sha256_alg registered!");
+ }
+
+ if (mode == 10) {
+ stm_dbg(debug,
+ "[u8500_hash_probe]: To register only sha1 and sha256"
+ " algorithms!");
+
+ ret = crypto_register_shash(&sha1_alg);
+ if (ret) {
+ stm_error("Could not register sha1_alg!");
+ goto out_clk;
+ }
+
+ ret = crypto_register_shash(&sha256_alg);
+ if (ret) {
+ stm_error("Could not register sha256_alg!");
+ goto out_unreg1_tmp;
+ }
+ }
+
+ stm_dbg(debug, "[u8500_hash_probe]: Calling platform_set_drvdata()!");
+ platform_set_drvdata(pdev, hash_drv_data);
+ return 0;
+
+ if (mode == 0) {
+out_unreg1:
+ crypto_unregister_shash(&sha1_alg);
+out_unreg2:
+ crypto_unregister_shash(&sha256_alg);
+out_unreg3:
+ crypto_unregister_shash(&hmac_sha1_alg);
+ }
+
+ if (mode == 10) {
+out_unreg1_tmp:
+ crypto_unregister_shash(&sha1_alg);
+ }
+
+out_clk:
+ clk_disable(hash_drv_data->clk);
+ clk_put(hash_drv_data->clk);
+
+out_unmap:
+ iounmap(hash_drv_data->base);
+
+out_free_mem:
+ release_mem_region(res->start, res->end - res->start + 1);
+
+out_kfree:
+ kfree(hash_drv_data);
+out:
+ return ret;
+}
+
+/**
+ * u8500_hash_remove - Function that removes the hash device from the platform.
+ * @pdev: The platform device
+ */
+static int u8500_hash_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct hash_driver_data *hash_drv_data;
+
+ stm_dbg(debug, "[u8500_hash_remove]: (pdev=0x%x)", (u32) pdev);
+
+ stm_dbg(debug, "[u8500_hash_remove]: Calling platform_get_drvdata()!");
+ hash_drv_data = platform_get_drvdata(pdev);
+
+ if (mode == 0) {
+ stm_dbg(debug,
+ "[u8500_hash_remove]: To unregister all algorithms!");
+ crypto_unregister_shash(&sha1_alg);
+ crypto_unregister_shash(&sha256_alg);
+ crypto_unregister_shash(&hmac_sha1_alg);
+ crypto_unregister_shash(&hmac_sha256_alg);
+ }
+
+ if (mode == 10) {
+ stm_dbg(debug,
+ "[u8500_hash_remove]: To unregister only sha1 and "
+ "sha256 algorithms!");
+ crypto_unregister_shash(&sha1_alg);
+ crypto_unregister_shash(&sha256_alg);
+ }
+#ifdef HASH_ACC_SYNC_CONTROL
+ stm_dbg(debug, "[u8500_hash_remove]: Calling mutex_destroy()!");
+ mutex_destroy(&hash_hw_acc_mutex);
+#endif
+
+ stm_dbg(debug, "[u8500_hash_remove]: Calling clk_disable()!");
+ clk_disable(hash_drv_data->clk);
+
+ stm_dbg(debug, "[u8500_hash_remove]: Calling clk_put()!");
+ clk_put(hash_drv_data->clk);
+
+ stm_dbg(debug, "[u8500_hash_remove]: Calling iounmap(): base = 0x%x",
+ (u32) hash_drv_data->base);
+ iounmap(hash_drv_data->base);
+
+ stm_dbg(debug, "[u8500_hash_remove]: Calling platform_get_resource()!");
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ stm_dbg(debug,
+ "[u8500_hash_remove]: Calling release_mem_region()"
+ "->res->start=0x%x, res->end = 0x%x!",
+ res->start, res->end);
+ release_mem_region(res->start, res->end - res->start + 1);
+
+ stm_dbg(debug, "[u8500_hash_remove]: Calling kfree()!");
+ kfree(hash_drv_data);
+
+ return 0;
+}
+
+static struct platform_driver hash_driver = {
+ .probe = u8500_hash_probe,
+ .remove = u8500_hash_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "hash1",
+ },
+};
+
+/**
+ * u8500_hash_mod_init - The kernel module init function.
+ */
+static int __init u8500_hash_mod_init(void)
+{
+ stm_dbg(debug, "u8500_hash_mod_init() is called!");
+
+ return platform_driver_register(&hash_driver);
+}
+
+/**
+ * u8500_hash_mod_fini - The kernel module exit function.
+ */
+static void __exit u8500_hash_mod_fini(void)
+{
+ stm_dbg(debug, "u8500_hash_mod_fini() is called!");
+
+ platform_driver_unregister(&hash_driver);
+ return;
+}
+
+/**
+ * hash_processblock - This function processes a single block of 512 bits (64
+ * bytes), word aligned, starting at message.
+ * @hid: Hardware device ID
+ * @message: Block (512 bits) of message to be written to the HASH hardware
+ *
+ * Reentrancy: Non Re-entrant.
+ */
+static void hash_processblock(int hid, const u32 *message)
+{
+ u32 count;
+
+ HCL_CLEAR_BITS(g_sys_ctx.registry[hid]->str,
+ HASH_STR_DCAL_MASK);
+ HCL_CLEAR_BITS(g_sys_ctx.registry[hid]->str,
+ HASH_STR_NBLW_MASK);
+
+ /* Partially unrolled loop */
+ for (count = 0; count < (HASH_BLOCK_SIZE / sizeof(u32));
+ count += 4) {
+ HASH_SET_DIN(message[0]);
+ HASH_SET_DIN(message[1]);
+ HASH_SET_DIN(message[2]);
+ HASH_SET_DIN(message[3]);
+ message += 4;
+ }
+}
+
+/**
+ * hash_messagepad - Pads a message and write the nblw bits.
+ * @hid: Hardware device ID
+ * @message: Last word of a message
+ * @index_bytes: The number of bytes in the last message
+ *
+ * This function manages the final part of the digest calculation, when less
+ * than 512 bits (64 bytes) remain in message. This means index_bytes < 64.
+ *
+ * Reentrancy: Non Re-entrant.
+ */
+static void hash_messagepad(int hid, const u32 *message, u8 index_bytes)
+{
+ stm_dbg(debug, "[u8500_hash_alg] hash_messagepad"
+ "(bytes in final msg=%d))", index_bytes);
+
+ clear_reg_str(hid);
+
+ /* Main loop */
+ while (index_bytes >= 4) {
+ HASH_SET_DIN(message[0]);
+ index_bytes -= 4;
+ message++;
+ }
+
+ if (index_bytes)
+ HASH_SET_DIN(message[0]);
+
+ /* num_of_bytes == 0 => NBLW <- 0 (32 bits valid in DATAIN) */
+ HASH_SET_NBLW(index_bytes * 8);
+ stm_dbg(debug, "[u8500_hash_alg] hash_messagepad -> DIN=0x%08x NBLW=%d",
+ g_sys_ctx.registry[hid]->din,
+ g_sys_ctx.registry[hid]->str);
+ HASH_SET_DCAL;
+ stm_dbg(debug, "[u8500_hash_alg] hash_messagepad d -> "
+ "DIN=0x%08x NBLW=%d",
+ g_sys_ctx.registry[hid]->din,
+ g_sys_ctx.registry[hid]->str);
+
+}
+
+/**
+ * hash_incrementlength - Increments the length of the current message.
+ * @hid: Hardware device ID
+ * @incr: Length of message processed already
+ *
+ * Overflow cannot occur, because conditions for overflow are checked in
+ * hash_hw_update.
+ */
+static void hash_incrementlength(int hid, u32 incr)
+{
+ g_sys_ctx.state[hid].length.low_word += incr;
+
+ /* Check for wrap-around */
+ if (g_sys_ctx.state[hid].length.low_word < incr)
+ g_sys_ctx.state[hid].length.high_word++;
+}
+
+/**
+ * hash_setconfiguration - Sets the required configuration for the hash
+ * hardware.
+ * @hid: Hardware device ID
+ * @p_config: Pointer to a configuration structure
+ *
+ * Reentrancy: Non Re-entrant
+ * Reentrancy issues:
+ * 1. Global variable registry(cofiguration register,
+ * parameter register, divider register) is being modified
+ *
+ * Comments 1. : User need to call hash_begin API after calling this
+ * API i.e. the current configuration is set only when
+ * bit INIT is set and we set INIT bit in hash_begin.
+ * Changing the configuration during a computation has
+ * no effect so we first set configuration by calling
+ * this API and then set the INIT bit for the HASH
+ * processor and the curent configuration is taken into
+ * account. As reading INIT bit (with correct protection
+ * rights) will always return 0b so we can't make a check
+ * at software level. So the user has to initialize the
+ * device for new configuration to take in to effect.
+ * 2. The default value of data format is 00b ie the format
+ * of data entered in HASH_DIN register is 32-bit data.
+ * The data written in HASH_DIN is used directly by the
+ * HASH processing, without re ordering.
+ */
+int hash_setconfiguration(int hid, struct hash_config *p_config)
+{
+ int hash_error = HASH_OK;
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_setconfiguration())");
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ HASH_SET_DATA_FORMAT(p_config->data_format);
+
+ HCL_SET_BITS(g_sys_ctx.registry[hid]->cr,
+ HASH_CR_EMPTYMSG_MASK);
+
+ /* This bit selects between SHA-1 or SHA-2 algorithm */
+ if (HASH_ALGO_SHA2 == p_config->algorithm) {
+ HCL_CLEAR_BITS(g_sys_ctx.registry[hid]->cr,
+ HASH_CR_ALGO_MASK);
+ } else { /* SHA1 algorithm */
+
+ HCL_SET_BITS(g_sys_ctx.registry[hid]->cr,
+ HASH_CR_ALGO_MASK);
+ }
+
+ /* This bit selects between HASH or HMAC mode for the selected
+ algorithm */
+ if (HASH_OPER_MODE_HASH == p_config->oper_mode) {
+ HCL_CLEAR_BITS(g_sys_ctx.registry
+ [hid]->cr, HASH_CR_MODE_MASK);
+ } else { /* HMAC mode */
+
+ HCL_SET_BITS(g_sys_ctx.registry[hid]->cr,
+ HASH_CR_MODE_MASK);
+
+ /* This bit selects between short key (<= 64 bytes) or long key
+ (>64 bytes) in HMAC mode */
+ if (HASH_SHORT_KEY == p_config->hmac_key) {
+ HCL_CLEAR_BITS(g_sys_ctx.registry[hid]->cr,
+ HASH_CR_LKEY_MASK);
+ } else {
+ HCL_SET_BITS(g_sys_ctx.registry[hid]->cr,
+ HASH_CR_LKEY_MASK);
+ }
+ }
+
+ return hash_error;
+}
+
+/**
+ * hash_begin - This routine resets some globals and initializes the hash
+ * hardware.
+ * @hid: Hardware device ID
+ *
+ * Reentrancy: Non Re-entrant
+ *
+ * Comments 1. : User need to call hash_setconfiguration API before
+ * calling this API i.e. the current configuration is set
+ * only when bit INIT is set and we set INIT bit in
+ * hash_begin. Changing the configuration during a
+ * computation has no effect so we first set
+ * configuration by calling this API and then set the
+ * INIT bit for the HASH processor and the current
+ * configuration is taken into account. As reading INIT
+ * bit (with correct protection rights) will always
+ * return 0b so we can't make a check at software level.
+ * So the user has to initialize the device for new
+ * configuration to take in to effect.
+ */
+int hash_begin(int hid)
+{
+ int hash_error = HASH_OK;
+
+ /* HW and SW initializations */
+ /* Note: there is no need to initialize buffer and digest members */
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_begin())");
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ g_sys_ctx.state[hid].index = 0;
+ g_sys_ctx.state[hid].bit_index = 0;
+ g_sys_ctx.state[hid].length.high_word = 0;
+ g_sys_ctx.state[hid].length.low_word = 0;
+
+ HASH_INITIALIZE;
+
+ HCL_CLEAR_BITS(g_sys_ctx.registry[hid]->str,
+ HASH_STR_DCAL_MASK);
+ HCL_CLEAR_BITS(g_sys_ctx.registry[hid]->str,
+ HASH_STR_NBLW_MASK);
+
+ return hash_error;
+}
+
+/**
+ * hash_hw_update - Updates current HASH computation hashing another part of
+ * the message.
+ * @hid: Hardware device ID
+ * @p_data_buffer: Byte array containing the message to be hashed (caller
+ * allocated)
+ * @msg_length: Length of message to be hashed (in bits)
+ *
+ * Reentrancy: Non Re-entrant
+ */
+int hash_hw_update(int hid, const u8 *p_data_buffer, u32 msg_length)
+{
+ int hash_error = HASH_OK;
+ u8 index;
+ u8 *p_buffer;
+ u32 count;
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_hw_update(msg_length=%d / %d), "
+ "in=%d, bin=%d))",
+ msg_length,
+ msg_length / 8,
+ g_sys_ctx.state[hid].index,
+ g_sys_ctx.state[hid].bit_index);
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ index = g_sys_ctx.state[hid].index;
+
+ p_buffer = (u8 *)g_sys_ctx.state[hid].buffer;
+
+ /* Number of bytes in the message */
+ msg_length /= 8;
+
+ /* Check parameters */
+ if (NULL == p_data_buffer) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ /* Check if g_sys_ctx.state.length + msg_length
+ overflows */
+ if (msg_length >
+ (g_sys_ctx.state[hid].length.low_word + msg_length)
+ && HASH_HIGH_WORD_MAX_VAL ==
+ (g_sys_ctx.state[hid].length.high_word)) {
+ hash_error = HASH_MSG_LENGTH_OVERFLOW;
+ stm_error("[u8500_hash_alg] HASH_MSG_LENGTH_OVERFLOW!");
+ return hash_error;
+ }
+
+ /* Main loop */
+ while (0 != msg_length) {
+ if ((index + msg_length) < HASH_BLOCK_SIZE) {
+ for (count = 0; count < msg_length; count++) {
+ /*TODO: memcpy? */
+ p_buffer[index + count] =
+ *(p_data_buffer + count);
+ }
+
+ index += msg_length;
+ msg_length = 0;
+ } else {
+ /* if 'p_data_buffer' is four byte aligned and local
+ * buffer does not have any data, we can write data
+ * directly from 'p_data_buffer' to HW peripheral,
+ * otherwise we first copy data to a local buffer
+ */
+ if ((0 == (((u32) p_data_buffer) % 4))
+ && (0 == index)) {
+ hash_processblock(hid,
+ (const u32 *)p_data_buffer);
+ } else {
+ for (count = 0;
+ count < (u32)(HASH_BLOCK_SIZE - index);
+ count++) {
+ p_buffer[index + count] =
+ *(p_data_buffer + count);
+ }
+
+ hash_processblock(hid, (const u32 *)p_buffer);
+ }
+
+ hash_incrementlength(hid, HASH_BLOCK_SIZE);
+ p_data_buffer += (HASH_BLOCK_SIZE - index);
+ msg_length -= (HASH_BLOCK_SIZE - index);
+ index = 0;
+ }
+ }
+
+ g_sys_ctx.state[hid].index = index;
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_hw_update END(msg_length=%d in "
+ "bits, in=%d, bin=%d))",
+ msg_length,
+ g_sys_ctx.state[hid].index,
+ g_sys_ctx.state[hid].bit_index);
+
+ return hash_error;
+}
+
+/**
+ * hash_end_key - Function that ends a message, i.e. pad and triggers the last
+ * calculation.
+ * @hid: Hardware device ID
+ *
+ * This function also clear the registries that have been involved in
+ * computation.
+ */
+int hash_end_key(int hid)
+{
+ int hash_error = HASH_OK;
+ u8 count = 0;
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_end_key(index=%d))",
+ g_sys_ctx.state[hid].index);
+
+ hash_messagepad(hid, g_sys_ctx.state[hid].buffer,
+ g_sys_ctx.state[hid].index);
+
+ /* Wait till the DCAL bit get cleared, So that we get the final
+ * message digest not intermediate value.
+ */
+ while (g_sys_ctx.registry[hid]->str & HASH_STR_DCAL_MASK)
+ ;
+
+ /* Reset the HASH state */
+ g_sys_ctx.state[hid].index = 0;
+ g_sys_ctx.state[hid].bit_index = 0;
+
+ for (count = 0; count < HASH_BLOCK_SIZE / sizeof(u32); count++)
+ g_sys_ctx.state[hid].buffer[count] = 0;
+
+ g_sys_ctx.state[hid].length.high_word = 0;
+ g_sys_ctx.state[hid].length.low_word = 0;
+
+ return hash_error;
+}
+
+/**
+ * hash_resume_state - Function that resumes the state of an calculation.
+ * @hid: Hardware device ID
+ * @device_state: The state to be restored in the hash hardware
+ *
+ * Reentrancy: Non Re-entrant
+ */
+int hash_resume_state(int hid, const struct hash_state *device_state)
+{
+ u32 temp_cr;
+ int hash_error = HASH_OK;
+ s32 count;
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_resume_state(state(0x%x)))",
+ (u32) device_state);
+
+ if (NULL == device_state) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ /* Check correctness of index and length members */
+ if (device_state->index > HASH_BLOCK_SIZE
+ || (device_state->length.low_word % HASH_BLOCK_SIZE) != 0) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ for (count = 0; count < (s32) (HASH_BLOCK_SIZE / sizeof(u32));
+ count++) {
+ g_sys_ctx.state[hid].buffer[count] =
+ device_state->buffer[count];
+ }
+
+ g_sys_ctx.state[hid].index = device_state->index;
+ g_sys_ctx.state[hid].bit_index = device_state->bit_index;
+ g_sys_ctx.state[hid].length = device_state->length;
+
+ HASH_INITIALIZE;
+
+ temp_cr = device_state->temp_cr;
+ g_sys_ctx.registry[hid]->cr =
+ temp_cr & HASH_CR_RESUME_MASK;
+
+ for (count = 0; count < HASH_CSR_COUNT; count++) {
+ if ((count >= 36) &&
+ !(g_sys_ctx.registry[hid]->cr &
+ HASH_CR_MODE_MASK)) {
+ break;
+ }
+ g_sys_ctx.registry[hid]->csrx[count] =
+ device_state->csr[count];
+ }
+
+ g_sys_ctx.registry[hid]->csfull = device_state->csfull;
+ g_sys_ctx.registry[hid]->csdatain = device_state->csdatain;
+
+ g_sys_ctx.registry[hid]->str = device_state->str_reg;
+ g_sys_ctx.registry[hid]->cr = temp_cr;
+
+ return hash_error;
+}
+
+/**
+ * hash_save_state - Function that saves the state of hardware.
+ * @hid: Hardware device ID
+ * @device_state: The strucure where the hardware state should be saved
+ *
+ * Reentrancy: Non Re-entrant
+ */
+int hash_save_state(int hid, struct hash_state *device_state)
+{
+ u32 temp_cr;
+ u32 count;
+ int hash_error = HASH_OK;
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_save_state( state(0x%x)))",
+ (u32) device_state);
+
+ if (NULL == device_state) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ for (count = 0; count < HASH_BLOCK_SIZE / sizeof(u32); count++) {
+ device_state->buffer[count] =
+ g_sys_ctx.state[hid].buffer[count];
+ }
+
+ device_state->index = g_sys_ctx.state[hid].index;
+ device_state->bit_index = g_sys_ctx.state[hid].bit_index;
+ device_state->length = g_sys_ctx.state[hid].length;
+
+ /* Write dummy value to force digest intermediate calculation. This
+ * actually makes sure that there isn't any ongoing calculation in the
+ * hardware.
+ */
+ while (g_sys_ctx.registry[hid]->str & HASH_STR_DCAL_MASK)
+ ;
+
+ temp_cr = g_sys_ctx.registry[hid]->cr;
+
+ device_state->str_reg = g_sys_ctx.registry[hid]->str;
+
+ device_state->din_reg = g_sys_ctx.registry[hid]->din;
+
+ for (count = 0; count < HASH_CSR_COUNT; count++) {
+ if ((count >= 36)
+ && !(g_sys_ctx.registry[hid]->cr &
+ HASH_CR_MODE_MASK)) {
+ break;
+ }
+
+ device_state->csr[count] =
+ g_sys_ctx.registry[hid]->csrx[count];
+ }
+
+ device_state->csfull = g_sys_ctx.registry[hid]->csfull;
+ device_state->csdatain = g_sys_ctx.registry[hid]->csdatain;
+
+ /* end if */
+ device_state->temp_cr = temp_cr;
+
+ return hash_error;
+}
+
+/**
+ * hash_end - Ends current HASH computation, passing back the hash to the user.
+ * @hid: Hardware device ID
+ * @digest: User allocated byte array for the calculated digest
+ *
+ * Reentrancy: Non Re-entrant
+ */
+int hash_end(int hid, u8 digest[HASH_MSG_DIGEST_SIZE])
+{
+ int hash_error = HASH_OK;
+ u32 count;
+ /* Standard SHA-1 digest for null string for HASH mode */
+ u8 zero_message_hash_sha1[HASH_MSG_DIGEST_SIZE] = {
+ 0xDA, 0x39, 0xA3, 0xEE,
+ 0x5E, 0x6B, 0x4B, 0x0D,
+ 0x32, 0x55, 0xBF, 0xEF,
+ 0x95, 0x60, 0x18, 0x90,
+ 0xAF, 0xD8, 0x07, 0x09,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ /* Standard SHA-2 digest for null string for HASH mode */
+ u8 zero_message_hash_sha2[HASH_MSG_DIGEST_SIZE] = {
+ 0xD4, 0x1D, 0x8C, 0xD9,
+ 0x8F, 0x00, 0xB2, 0x04,
+ 0xE9, 0x80, 0x09, 0x98,
+ 0xEC, 0xF8, 0x42, 0x7E,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ /* Standard SHA-1 digest for null string for HMAC mode,with no key */
+ u8 zero_message_hmac_sha1[HASH_MSG_DIGEST_SIZE] = {
+ 0xFB, 0xDB, 0x1D, 0x1B,
+ 0x18, 0xAA, 0x6C, 0x08,
+ 0x32, 0x4B, 0x7D, 0x64,
+ 0xB7, 0x1F, 0xB7, 0x63,
+ 0x70, 0x69, 0x0E, 0x1D,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ /* Standard SHA2 digest for null string for HMAC mode,with no key */
+ u8 zero_message_hmac_sha2[HASH_MSG_DIGEST_SIZE] = {
+ 0x74, 0xE6, 0xF7, 0x29,
+ 0x8A, 0x9C, 0x2D, 0x16,
+ 0x89, 0x35, 0xF5, 0x8C,
+ 0x00, 0x1B, 0xAD, 0x88,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_end(digest array (0x%x)))",
+ (u32) digest);
+
+ if (NULL == digest) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ if (0 == g_sys_ctx.state[hid].index &&
+ 0 == g_sys_ctx.state[hid].length.high_word &&
+ 0 == g_sys_ctx.state[hid].length.low_word) {
+ if (g_sys_ctx.registry[hid]->cr & HASH_CR_MODE_MASK) {
+ if (g_sys_ctx.registry[hid]->cr & HASH_CR_ALGO_MASK) {
+ /* hash of an empty message was requested */
+ for (count = 0; count < HASH_MSG_DIGEST_SIZE;
+ count++) {
+ digest[count] =
+ zero_message_hmac_sha1[count];
+ }
+ } else { /* SHA-2 algo */
+
+ /* hash of an empty message was requested */
+ for (count = 0; count < HASH_MSG_DIGEST_SIZE;
+ count++) {
+ digest[count] =
+ zero_message_hmac_sha2[count];
+ }
+ }
+ } else { /* HASH mode */
+
+ if (g_sys_ctx.registry[hid]->cr & HASH_CR_ALGO_MASK) {
+ /* hash of an empty message was requested */
+ for (count = 0; count < HASH_MSG_DIGEST_SIZE;
+ count++) {
+ digest[count] =
+ zero_message_hash_sha1[count];
+ }
+ } else { /* SHA-2 algo */
+
+ /* hash of an empty message was requested */
+ for (count = 0; count < HASH_MSG_DIGEST_SIZE;
+ count++) {
+ digest[count] =
+ zero_message_hash_sha2[count];
+ }
+ }
+ }
+
+ HASH_SET_DCAL;
+ } else {
+ hash_messagepad(hid,
+ g_sys_ctx.state[hid].buffer,
+ g_sys_ctx.state[hid].index);
+
+ /* Wait till the DCAL bit get cleared, So that we get the final
+ * message digest not intermediate value. */
+ while (g_sys_ctx.registry[hid]->str & HASH_STR_DCAL_MASK)
+ ;
+
+ hash_error = hash_get_digest(hid, digest);
+
+ /* Reset the HASH state */
+ g_sys_ctx.state[hid].index = 0;
+ g_sys_ctx.state[hid].bit_index = 0;
+ for (count = 0; count < HASH_BLOCK_SIZE / sizeof(u32);
+ count++) {
+ g_sys_ctx.state[hid].buffer[count]
+ = 0;
+ }
+
+ g_sys_ctx.state[hid].length.high_word = 0;
+ g_sys_ctx.state[hid].length.low_word = 0;
+ }
+
+ if (debug)
+ hexdump(digest, HASH_MSG_DIGEST_SIZE);
+
+ return hash_error;
+}
+
+/**
+ * hash_initialize_globals - Initialize global variables to their default reset
+ * value.
+ * @hid: Hardware device ID
+ *
+ * Reentrancy: Non Re-entrant, global structure g_sys_ctx elements are being
+ * modified
+ */
+static void hash_initialize_globals(int hid)
+{
+ u8 loop_count;
+
+ /* Resetting the values of global variables except the registry */
+ g_sys_ctx.state[hid].temp_cr = HASH_RESET_INDEX_VAL;
+ g_sys_ctx.state[hid].str_reg = HASH_RESET_INDEX_VAL;
+ g_sys_ctx.state[hid].din_reg = HASH_RESET_INDEX_VAL;
+
+ for (loop_count = 0; loop_count < HASH_CSR_COUNT; loop_count++) {
+ g_sys_ctx.state[hid].csr[loop_count] =
+ HASH_RESET_CSRX_REG_VALUE;
+ }
+
+ g_sys_ctx.state[hid].csfull = HASH_RESET_CSFULL_REG_VALUE;
+ g_sys_ctx.state[hid].csdatain = HASH_RESET_CSDATAIN_REG_VALUE;
+
+ for (loop_count = 0; loop_count < (HASH_BLOCK_SIZE / sizeof(u32));
+ loop_count++) {
+ g_sys_ctx.state[hid].buffer[loop_count] =
+ HASH_RESET_BUFFER_VAL;
+ }
+
+ g_sys_ctx.state[hid].length.high_word = HASH_RESET_LEN_HIGH_VAL;
+ g_sys_ctx.state[hid].length.low_word = HASH_RESET_LEN_LOW_VAL;
+ g_sys_ctx.state[hid].index = HASH_RESET_INDEX_VAL;
+ g_sys_ctx.state[hid].bit_index = HASH_RESET_BIT_INDEX_VAL;
+}
+
+/**
+ * hash_reset - This routine will reset the global variable to default reset
+ * value and HASH registers to their power on reset values.
+ * @hid: Hardware device ID
+ *
+ * Reentrancy: Non Re-entrant, global structure g_sys_ctx elements are being
+ * modified.
+ */
+int hash_reset(int hid)
+{
+ int hash_error = HASH_OK;
+ u8 loop_count;
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+
+ return hash_error;
+ }
+
+ /* Resetting the values of global variables except the registry */
+ hash_initialize_globals(hid);
+
+ /* Resetting HASH control register to power-on-reset values */
+ g_sys_ctx.registry[hid]->str = HASH_RESET_START_REG_VALUE;
+
+ for (loop_count = 0; loop_count < HASH_CSR_COUNT; loop_count++) {
+ g_sys_ctx.registry[hid]->csrx[loop_count] =
+ HASH_RESET_CSRX_REG_VALUE;
+ }
+
+ g_sys_ctx.registry[hid]->csfull = HASH_RESET_CSFULL_REG_VALUE;
+ g_sys_ctx.registry[hid]->csdatain =
+ HASH_RESET_CSDATAIN_REG_VALUE;
+
+ /* Resetting the HASH Control reg. This also reset the PRIVn and SECn
+ * bits and hence the device registers will not be accessed anymore and
+ * should be done in the last HASH register access statement.
+ */
+ g_sys_ctx.registry[hid]->cr = HASH_RESET_CONTROL_REG_VALUE;
+
+ return hash_error;
+}
+
+/**
+ * hash_init_base_address - This routine initializes hash register base
+ * address. It also checks for peripheral Ids and PCell Ids.
+ * @hid: Hardware device ID
+ * @base_address: Hash hardware base address
+ *
+ * Reentrancy: Non Re-entrant, global variable registry (register base address)
+ * is being modified.
+ */
+int hash_init_base_address(int hid, t_logical_address base_address)
+{
+ int hash_error = HASH_OK;
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_init_base_address())");
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+
+ return hash_error;
+ }
+
+ if (0 != base_address) {
+ /*--------------------------------------*
+ * Initializing the registers structure *
+ *--------------------------------------*/
+ g_sys_ctx.registry[hid] = (struct hash_register *) base_address;
+
+ /*--------------------------*
+ * Checking Peripheral Ids *
+ *--------------------------*/
+ if ((HASH_P_ID0 ==
+ g_sys_ctx.registry[hid]->periphid0)
+ && (HASH_P_ID1 ==
+ g_sys_ctx.registry[hid]->periphid1)
+ && (HASH_P_ID2 ==
+ g_sys_ctx.registry[hid]->periphid2)
+ && (HASH_P_ID3 ==
+ g_sys_ctx.registry[hid]->periphid3)
+ && (HASH_CELL_ID0 ==
+ g_sys_ctx.registry[hid]->cellid0)
+ && (HASH_CELL_ID1 ==
+ g_sys_ctx.registry[hid]->cellid1)
+ && (HASH_CELL_ID2 ==
+ g_sys_ctx.registry[hid]->cellid2)
+ && (HASH_CELL_ID3 ==
+ g_sys_ctx.registry[hid]->cellid3)
+ ) {
+
+ /* Resetting the values of global variables except the
+ registry */
+ hash_initialize_globals(hid);
+ hash_error = HASH_OK;
+ return hash_error;
+ } else {
+ hash_error = HASH_UNSUPPORTED_HW;
+ stm_error("[u8500_hash_alg] HASH_UNSUPPORTED_HW!");
+ return hash_error;
+ }
+ } /* end if */
+ else {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+}
+
+/**
+ * hash_get_digest - Gets the digest.
+ * @hid: Hardware device ID
+ * @digest: User allocated byte array for the calculated digest
+ *
+ * Reentrancy: Non Re-entrant, global variable registry (hash control register)
+ * is being modified.
+ *
+ * Note that, if this is called before the final message has been handle it will
+ * return the intermediate message digest.
+ */
+int hash_get_digest(int hid, u8 *digest)
+{
+ u32 temp_hx_val, count;
+ int hash_error = HASH_OK;
+
+ stm_dbg(debug,
+ "[u8500_hash_alg] hash_get_digest(digest array:(0x%x))",
+ (u32) digest);
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+ /* Copy result into digest array */
+ for (count = 0; count < (HASH_MSG_DIGEST_SIZE / sizeof(u32));
+ count++) {
+ temp_hx_val = HASH_GET_HX(count);
+ digest[count * 4] = (u8) ((temp_hx_val >> 24) & 0xFF);
+ digest[count * 4 + 1] = (u8) ((temp_hx_val >> 16) & 0xFF);
+ digest[count * 4 + 2] = (u8) ((temp_hx_val >> 8) & 0xFF);
+ digest[count * 4 + 3] = (u8) ((temp_hx_val >> 0) & 0xFF);
+ }
+
+ return hash_error;
+}
+
+/**
+ * hash_compute - Performs a complete HASH calculation on the message passed.
+ * @hid: Hardware device ID
+ * @p_data_buffer: Pointer to the message to be hashed
+ * @msg_length: The length of the message
+ * @p_hash_config: Structure with configuration data for the hash hardware
+ * @digest: User allocated byte array for the calculated digest
+ *
+ * Reentrancy: Non Re-entrant
+ */
+int hash_compute(int hid,
+ const u8 *p_data_buffer,
+ u32 msg_length,
+ struct hash_config *p_hash_config,
+ u8 digest[HASH_MSG_DIGEST_SIZE]) {
+ int hash_error = HASH_OK;
+
+ stm_dbg(debug, "[u8500_hash_alg] hash_compute())");
+
+ if (!((HASH_DEVICE_ID_0 == hid)
+ || (HASH_DEVICE_ID_1 == hid))) {
+ hash_error = HASH_INVALID_PARAMETER;
+ stm_error("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_error;
+ }
+
+
+ /* WARNING: return code must be checked if
+ * behaviour of hash_begin changes.
+ */
+ hash_error = hash_setconfiguration(hid, p_hash_config);
+ if (HASH_OK != hash_error) {
+ stm_error("[u8500_hash_alg] hash_setconfiguration() failed!");
+ return hash_error;
+ }
+
+ hash_error = hash_begin(hid);
+ if (HASH_OK != hash_error) {
+ stm_error("[u8500_hash_alg] hash_begin() failed!");
+ return hash_error;
+ }
+
+ hash_error = hash_hw_update(hid, p_data_buffer, msg_length);
+ if (HASH_OK != hash_error) {
+ stm_error("[u8500_hash_alg] hash_hw_update() failed!");
+ return hash_error;
+ }
+
+ hash_error = hash_end(hid, digest);
+ if (HASH_OK != hash_error) {
+ stm_error("[u8500_hash_alg] hash_end() failed!");
+ return hash_error;
+ }
+
+ return hash_error;
+}
+
+module_init(u8500_hash_mod_init);
+module_exit(u8500_hash_mod_fini);
+
+module_param(mode, int, 0);
+module_param(debug, int, 0);
+module_param(contextsaving, int, 0);
+
+MODULE_DESCRIPTION("Driver for ST-Ericsson U8500 HASH engine.");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("sha1-u8500");
+MODULE_ALIAS("sha256-u8500");
+MODULE_ALIAS("hmac(sha1-u8500)");
+MODULE_ALIAS("hmac(sha256-u8500)");