linux/drivers/crypto/ux500/hash/hash_core.c
Linus Torvalds d5acba26bf Char/Misc driver patches for 4.19-rc1
Here is the bit set of char/misc drivers for 4.19-rc1
 
 There is a lot here, much more than normal, seems like everyone is
 writing new driver subsystems these days...  Anyway, major things here
 are:
 	- new FSI driver subsystem, yet-another-powerpc low-level
 	  hardware bus
 	- gnss, finally an in-kernel GPS subsystem to try to tame all of
 	  the crazy out-of-tree drivers that have been floating around
 	  for years, combined with some really hacky userspace
 	  implementations.  This is only for GNSS receivers, but you
 	  have to start somewhere, and this is great to see.
 Other than that, there are new slimbus drivers, new coresight drivers,
 new fpga drivers, and loads of DT bindings for all of these and existing
 drivers.
 
 Full details of everything is in the shortlog.
 
 All of these have been in linux-next for a while with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCW3g7ew8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ykfBgCeOG0RkSI92XVZe0hs/QYFW9kk8JYAnRBf3Qpm
 cvW7a+McOoKz/MGmEKsi
 =TNfn
 -----END PGP SIGNATURE-----

Merge tag 'char-misc-4.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char/misc driver updates from Greg KH:
 "Here is the bit set of char/misc drivers for 4.19-rc1

  There is a lot here, much more than normal, seems like everyone is
  writing new driver subsystems these days... Anyway, major things here
  are:

   - new FSI driver subsystem, yet-another-powerpc low-level hardware
     bus

   - gnss, finally an in-kernel GPS subsystem to try to tame all of the
     crazy out-of-tree drivers that have been floating around for years,
     combined with some really hacky userspace implementations. This is
     only for GNSS receivers, but you have to start somewhere, and this
     is great to see.

  Other than that, there are new slimbus drivers, new coresight drivers,
  new fpga drivers, and loads of DT bindings for all of these and
  existing drivers.

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'char-misc-4.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (255 commits)
  android: binder: Rate-limit debug and userspace triggered err msgs
  fsi: sbefifo: Bump max command length
  fsi: scom: Fix NULL dereference
  misc: mic: SCIF Fix scif_get_new_port() error handling
  misc: cxl: changed asterisk position
  genwqe: card_base: Use true and false for boolean values
  misc: eeprom: assignment outside the if statement
  uio: potential double frees if __uio_register_device() fails
  eeprom: idt_89hpesx: clean up an error pointer vs NULL inconsistency
  misc: ti-st: Fix memory leak in the error path of probe()
  android: binder: Show extra_buffers_size in trace
  firmware: vpd: Fix section enabled flag on vpd_section_destroy
  platform: goldfish: Retire pdev_bus
  goldfish: Use dedicated macros instead of manual bit shifting
  goldfish: Add missing includes to goldfish.h
  mux: adgs1408: new driver for Analog Devices ADGS1408/1409 mux
  dt-bindings: mux: add adi,adgs1408
  Drivers: hv: vmbus: Cleanup synic memory free path
  Drivers: hv: vmbus: Remove use of slow_virt_to_phys()
  Drivers: hv: vmbus: Reset the channel callback in vmbus_onoffer_rescind()
  ...
2018-08-18 11:04:51 -07:00

1965 lines
50 KiB
C

/*
* Cryptographic API.
* Support for Nomadik hardware crypto engine.
* Copyright (C) ST-Ericsson SA 2010
* Author: Shujuan Chen <shujuan.chen@stericsson.com> for ST-Ericsson
* Author: Joakim Bech <joakim.xx.bech@stericsson.com> for ST-Ericsson
* Author: Berne Hebark <berne.herbark@stericsson.com> for ST-Ericsson.
* Author: Niklas Hernaeus <niklas.hernaeus@stericsson.com> for ST-Ericsson.
* Author: Andreas Westin <andreas.westin@stericsson.com> for ST-Ericsson.
* License terms: GNU General Public License (GPL) version 2
*/
#define pr_fmt(fmt) "hashX hashX: " fmt
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/klist.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/crypto.h>
#include <linux/regulator/consumer.h>
#include <linux/dmaengine.h>
#include <linux/bitops.h>
#include <crypto/internal/hash.h>
#include <crypto/sha.h>
#include <crypto/scatterwalk.h>
#include <crypto/algapi.h>
#include <linux/platform_data/crypto-ux500.h>
#include "hash_alg.h"
static int hash_mode;
module_param(hash_mode, int, 0);
MODULE_PARM_DESC(hash_mode, "CPU or DMA mode. CPU = 0 (default), DMA = 1");
/* HMAC-SHA1, no key */
static const u8 zero_message_hmac_sha1[SHA1_DIGEST_SIZE] = {
0xfb, 0xdb, 0x1d, 0x1b, 0x18, 0xaa, 0x6c, 0x08,
0x32, 0x4b, 0x7d, 0x64, 0xb7, 0x1f, 0xb7, 0x63,
0x70, 0x69, 0x0e, 0x1d
};
/* HMAC-SHA256, no key */
static const u8 zero_message_hmac_sha256[SHA256_DIGEST_SIZE] = {
0xb6, 0x13, 0x67, 0x9a, 0x08, 0x14, 0xd9, 0xec,
0x77, 0x2f, 0x95, 0xd7, 0x78, 0xc3, 0x5f, 0xc5,
0xff, 0x16, 0x97, 0xc4, 0x93, 0x71, 0x56, 0x53,
0xc6, 0xc7, 0x12, 0x14, 0x42, 0x92, 0xc5, 0xad
};
/**
* struct hash_driver_data - data specific to the driver.
*
* @device_list: A list of registered devices to choose from.
* @device_allocation: A semaphore initialized with number of devices.
*/
struct hash_driver_data {
struct klist device_list;
struct semaphore device_allocation;
};
static struct hash_driver_data driver_data;
/* Declaration of functions */
/**
* hash_messagepad - Pads a message and write the nblw bits.
* @device_data: Structure for the hash device.
* @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.
*
*/
static void hash_messagepad(struct hash_device_data *device_data,
const u32 *message, u8 index_bytes);
/**
* release_hash_device - Releases a previously allocated hash device.
* @device_data: Structure for the hash device.
*
*/
static void release_hash_device(struct hash_device_data *device_data)
{
spin_lock(&device_data->ctx_lock);
device_data->current_ctx->device = NULL;
device_data->current_ctx = NULL;
spin_unlock(&device_data->ctx_lock);
/*
* The down_interruptible part for this semaphore is called in
* cryp_get_device_data.
*/
up(&driver_data.device_allocation);
}
static void hash_dma_setup_channel(struct hash_device_data *device_data,
struct device *dev)
{
struct hash_platform_data *platform_data = dev->platform_data;
struct dma_slave_config conf = {
.direction = DMA_MEM_TO_DEV,
.dst_addr = device_data->phybase + HASH_DMA_FIFO,
.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
.dst_maxburst = 16,
};
dma_cap_zero(device_data->dma.mask);
dma_cap_set(DMA_SLAVE, device_data->dma.mask);
device_data->dma.cfg_mem2hash = platform_data->mem_to_engine;
device_data->dma.chan_mem2hash =
dma_request_channel(device_data->dma.mask,
platform_data->dma_filter,
device_data->dma.cfg_mem2hash);
dmaengine_slave_config(device_data->dma.chan_mem2hash, &conf);
init_completion(&device_data->dma.complete);
}
static void hash_dma_callback(void *data)
{
struct hash_ctx *ctx = data;
complete(&ctx->device->dma.complete);
}
static int hash_set_dma_transfer(struct hash_ctx *ctx, struct scatterlist *sg,
int len, enum dma_data_direction direction)
{
struct dma_async_tx_descriptor *desc = NULL;
struct dma_chan *channel = NULL;
dma_cookie_t cookie;
if (direction != DMA_TO_DEVICE) {
dev_err(ctx->device->dev, "%s: Invalid DMA direction\n",
__func__);
return -EFAULT;
}
sg->length = ALIGN(sg->length, HASH_DMA_ALIGN_SIZE);
channel = ctx->device->dma.chan_mem2hash;
ctx->device->dma.sg = sg;
ctx->device->dma.sg_len = dma_map_sg(channel->device->dev,
ctx->device->dma.sg, ctx->device->dma.nents,
direction);
if (!ctx->device->dma.sg_len) {
dev_err(ctx->device->dev, "%s: Could not map the sg list (TO_DEVICE)\n",
__func__);
return -EFAULT;
}
dev_dbg(ctx->device->dev, "%s: Setting up DMA for buffer (TO_DEVICE)\n",
__func__);
desc = dmaengine_prep_slave_sg(channel,
ctx->device->dma.sg, ctx->device->dma.sg_len,
direction, DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
if (!desc) {
dev_err(ctx->device->dev,
"%s: dmaengine_prep_slave_sg() failed!\n", __func__);
return -EFAULT;
}
desc->callback = hash_dma_callback;
desc->callback_param = ctx;
cookie = dmaengine_submit(desc);
dma_async_issue_pending(channel);
return 0;
}
static void hash_dma_done(struct hash_ctx *ctx)
{
struct dma_chan *chan;
chan = ctx->device->dma.chan_mem2hash;
dmaengine_terminate_all(chan);
dma_unmap_sg(chan->device->dev, ctx->device->dma.sg,
ctx->device->dma.sg_len, DMA_TO_DEVICE);
}
static int hash_dma_write(struct hash_ctx *ctx,
struct scatterlist *sg, int len)
{
int error = hash_set_dma_transfer(ctx, sg, len, DMA_TO_DEVICE);
if (error) {
dev_dbg(ctx->device->dev,
"%s: hash_set_dma_transfer() failed\n", __func__);
return error;
}
return len;
}
/**
* get_empty_message_digest - Returns a pre-calculated digest for
* the empty message.
* @device_data: Structure for the hash device.
* @zero_hash: Buffer to return the empty message digest.
* @zero_hash_size: Hash size of the empty message digest.
* @zero_digest: True if zero_digest returned.
*/
static int get_empty_message_digest(
struct hash_device_data *device_data,
u8 *zero_hash, u32 *zero_hash_size, bool *zero_digest)
{
int ret = 0;
struct hash_ctx *ctx = device_data->current_ctx;
*zero_digest = false;
/**
* Caller responsible for ctx != NULL.
*/
if (HASH_OPER_MODE_HASH == ctx->config.oper_mode) {
if (HASH_ALGO_SHA1 == ctx->config.algorithm) {
memcpy(zero_hash, &sha1_zero_message_hash[0],
SHA1_DIGEST_SIZE);
*zero_hash_size = SHA1_DIGEST_SIZE;
*zero_digest = true;
} else if (HASH_ALGO_SHA256 ==
ctx->config.algorithm) {
memcpy(zero_hash, &sha256_zero_message_hash[0],
SHA256_DIGEST_SIZE);
*zero_hash_size = SHA256_DIGEST_SIZE;
*zero_digest = true;
} else {
dev_err(device_data->dev, "%s: Incorrect algorithm!\n",
__func__);
ret = -EINVAL;
goto out;
}
} else if (HASH_OPER_MODE_HMAC == ctx->config.oper_mode) {
if (!ctx->keylen) {
if (HASH_ALGO_SHA1 == ctx->config.algorithm) {
memcpy(zero_hash, &zero_message_hmac_sha1[0],
SHA1_DIGEST_SIZE);
*zero_hash_size = SHA1_DIGEST_SIZE;
*zero_digest = true;
} else if (HASH_ALGO_SHA256 == ctx->config.algorithm) {
memcpy(zero_hash, &zero_message_hmac_sha256[0],
SHA256_DIGEST_SIZE);
*zero_hash_size = SHA256_DIGEST_SIZE;
*zero_digest = true;
} else {
dev_err(device_data->dev, "%s: Incorrect algorithm!\n",
__func__);
ret = -EINVAL;
goto out;
}
} else {
dev_dbg(device_data->dev,
"%s: Continue hash calculation, since hmac key available\n",
__func__);
}
}
out:
return ret;
}
/**
* hash_disable_power - Request to disable power and clock.
* @device_data: Structure for the hash device.
* @save_device_state: If true, saves the current hw state.
*
* This function request for disabling power (regulator) and clock,
* and could also save current hw state.
*/
static int hash_disable_power(struct hash_device_data *device_data,
bool save_device_state)
{
int ret = 0;
struct device *dev = device_data->dev;
spin_lock(&device_data->power_state_lock);
if (!device_data->power_state)
goto out;
if (save_device_state) {
hash_save_state(device_data,
&device_data->state);
device_data->restore_dev_state = true;
}
clk_disable(device_data->clk);
ret = regulator_disable(device_data->regulator);
if (ret)
dev_err(dev, "%s: regulator_disable() failed!\n", __func__);
device_data->power_state = false;
out:
spin_unlock(&device_data->power_state_lock);
return ret;
}
/**
* hash_enable_power - Request to enable power and clock.
* @device_data: Structure for the hash device.
* @restore_device_state: If true, restores a previous saved hw state.
*
* This function request for enabling power (regulator) and clock,
* and could also restore a previously saved hw state.
*/
static int hash_enable_power(struct hash_device_data *device_data,
bool restore_device_state)
{
int ret = 0;
struct device *dev = device_data->dev;
spin_lock(&device_data->power_state_lock);
if (!device_data->power_state) {
ret = regulator_enable(device_data->regulator);
if (ret) {
dev_err(dev, "%s: regulator_enable() failed!\n",
__func__);
goto out;
}
ret = clk_enable(device_data->clk);
if (ret) {
dev_err(dev, "%s: clk_enable() failed!\n", __func__);
ret = regulator_disable(
device_data->regulator);
goto out;
}
device_data->power_state = true;
}
if (device_data->restore_dev_state) {
if (restore_device_state) {
device_data->restore_dev_state = false;
hash_resume_state(device_data, &device_data->state);
}
}
out:
spin_unlock(&device_data->power_state_lock);
return ret;
}
/**
* hash_get_device_data - Checks for an available hash device and return it.
* @hash_ctx: Structure for the hash context.
* @device_data: Structure for the hash device.
*
* This function check for an available hash device and return it to
* the caller.
* Note! Caller need to release the device, calling up().
*/
static int hash_get_device_data(struct hash_ctx *ctx,
struct hash_device_data **device_data)
{
int ret;
struct klist_iter device_iterator;
struct klist_node *device_node;
struct hash_device_data *local_device_data = NULL;
/* Wait until a device is available */
ret = down_interruptible(&driver_data.device_allocation);
if (ret)
return ret; /* Interrupted */
/* Select a device */
klist_iter_init(&driver_data.device_list, &device_iterator);
device_node = klist_next(&device_iterator);
while (device_node) {
local_device_data = container_of(device_node,
struct hash_device_data, list_node);
spin_lock(&local_device_data->ctx_lock);
/* current_ctx allocates a device, NULL = unallocated */
if (local_device_data->current_ctx) {
device_node = klist_next(&device_iterator);
} else {
local_device_data->current_ctx = ctx;
ctx->device = local_device_data;
spin_unlock(&local_device_data->ctx_lock);
break;
}
spin_unlock(&local_device_data->ctx_lock);
}
klist_iter_exit(&device_iterator);
if (!device_node) {
/**
* No free device found.
* Since we allocated a device with down_interruptible, this
* should not be able to happen.
* Number of available devices, which are contained in
* device_allocation, is therefore decremented by not doing
* an up(device_allocation).
*/
return -EBUSY;
}
*device_data = local_device_data;
return 0;
}
/**
* hash_hw_write_key - Writes the key to the hardware registries.
*
* @device_data: Structure for the hash device.
* @key: Key to be written.
* @keylen: The lengt of the key.
*
* Note! This function DOES NOT write to the NBLW registry, even though
* specified in the the hw design spec. Either due to incorrect info in the
* spec or due to a bug in the hw.
*/
static void hash_hw_write_key(struct hash_device_data *device_data,
const u8 *key, unsigned int keylen)
{
u32 word = 0;
int nwords = 1;
HASH_CLEAR_BITS(&device_data->base->str, HASH_STR_NBLW_MASK);
while (keylen >= 4) {
u32 *key_word = (u32 *)key;
HASH_SET_DIN(key_word, nwords);
keylen -= 4;
key += 4;
}
/* Take care of the remaining bytes in the last word */
if (keylen) {
word = 0;
while (keylen) {
word |= (key[keylen - 1] << (8 * (keylen - 1)));
keylen--;
}
HASH_SET_DIN(&word, nwords);
}
while (readl(&device_data->base->str) & HASH_STR_DCAL_MASK)
cpu_relax();
HASH_SET_DCAL;
while (readl(&device_data->base->str) & HASH_STR_DCAL_MASK)
cpu_relax();
}
/**
* init_hash_hw - Initialise the hash hardware for a new calculation.
* @device_data: Structure for the hash device.
* @ctx: The hash context.
*
* This function will enable the bits needed to clear and start a new
* calculation.
*/
static int init_hash_hw(struct hash_device_data *device_data,
struct hash_ctx *ctx)
{
int ret = 0;
ret = hash_setconfiguration(device_data, &ctx->config);
if (ret) {
dev_err(device_data->dev, "%s: hash_setconfiguration() failed!\n",
__func__);
return ret;
}
hash_begin(device_data, ctx);
if (ctx->config.oper_mode == HASH_OPER_MODE_HMAC)
hash_hw_write_key(device_data, ctx->key, ctx->keylen);
return ret;
}
/**
* hash_get_nents - Return number of entries (nents) in scatterlist (sg).
*
* @sg: Scatterlist.
* @size: Size in bytes.
* @aligned: True if sg data aligned to work in DMA mode.
*
*/
static int hash_get_nents(struct scatterlist *sg, int size, bool *aligned)
{
int nents = 0;
bool aligned_data = true;
while (size > 0 && sg) {
nents++;
size -= sg->length;
/* hash_set_dma_transfer will align last nent */
if ((aligned && !IS_ALIGNED(sg->offset, HASH_DMA_ALIGN_SIZE)) ||
(!IS_ALIGNED(sg->length, HASH_DMA_ALIGN_SIZE) && size > 0))
aligned_data = false;
sg = sg_next(sg);
}
if (aligned)
*aligned = aligned_data;
if (size != 0)
return -EFAULT;
return nents;
}
/**
* hash_dma_valid_data - checks for dma valid sg data.
* @sg: Scatterlist.
* @datasize: Datasize in bytes.
*
* NOTE! This function checks for dma valid sg data, since dma
* only accept datasizes of even wordsize.
*/
static bool hash_dma_valid_data(struct scatterlist *sg, int datasize)
{
bool aligned;
/* Need to include at least one nent, else error */
if (hash_get_nents(sg, datasize, &aligned) < 1)
return false;
return aligned;
}
/**
* hash_init - Common hash init function for SHA1/SHA2 (SHA256).
* @req: The hash request for the job.
*
* Initialize structures.
*/
static int hash_init(struct ahash_request *req)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct hash_ctx *ctx = crypto_ahash_ctx(tfm);
struct hash_req_ctx *req_ctx = ahash_request_ctx(req);
if (!ctx->key)
ctx->keylen = 0;
memset(&req_ctx->state, 0, sizeof(struct hash_state));
req_ctx->updated = 0;
if (hash_mode == HASH_MODE_DMA) {
if (req->nbytes < HASH_DMA_ALIGN_SIZE) {
req_ctx->dma_mode = false; /* Don't use DMA */
pr_debug("%s: DMA mode, but direct to CPU mode for data size < %d\n",
__func__, HASH_DMA_ALIGN_SIZE);
} else {
if (req->nbytes >= HASH_DMA_PERFORMANCE_MIN_SIZE &&
hash_dma_valid_data(req->src, req->nbytes)) {
req_ctx->dma_mode = true;
} else {
req_ctx->dma_mode = false;
pr_debug("%s: DMA mode, but use CPU mode for datalength < %d or non-aligned data, except in last nent\n",
__func__,
HASH_DMA_PERFORMANCE_MIN_SIZE);
}
}
}
return 0;
}
/**
* hash_processblock - This function processes a single block of 512 bits (64
* bytes), word aligned, starting at message.
* @device_data: Structure for the hash device.
* @message: Block (512 bits) of message to be written to
* the HASH hardware.
*
*/
static void hash_processblock(struct hash_device_data *device_data,
const u32 *message, int length)
{
int len = length / HASH_BYTES_PER_WORD;
/*
* NBLW bits. Reset the number of bits in last word (NBLW).
*/
HASH_CLEAR_BITS(&device_data->base->str, HASH_STR_NBLW_MASK);
/*
* Write message data to the HASH_DIN register.
*/
HASH_SET_DIN(message, len);
}
/**
* hash_messagepad - Pads a message and write the nblw bits.
* @device_data: Structure for the hash device.
* @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.
*
*/
static void hash_messagepad(struct hash_device_data *device_data,
const u32 *message, u8 index_bytes)
{
int nwords = 1;
/*
* Clear hash str register, only clear NBLW
* since DCAL will be reset by hardware.
*/
HASH_CLEAR_BITS(&device_data->base->str, HASH_STR_NBLW_MASK);
/* Main loop */
while (index_bytes >= 4) {
HASH_SET_DIN(message, nwords);
index_bytes -= 4;
message++;
}
if (index_bytes)
HASH_SET_DIN(message, nwords);
while (readl(&device_data->base->str) & HASH_STR_DCAL_MASK)
cpu_relax();
/* num_of_bytes == 0 => NBLW <- 0 (32 bits valid in DATAIN) */
HASH_SET_NBLW(index_bytes * 8);
dev_dbg(device_data->dev, "%s: DIN=0x%08x NBLW=%lu\n",
__func__, readl_relaxed(&device_data->base->din),
readl_relaxed(&device_data->base->str) & HASH_STR_NBLW_MASK);
HASH_SET_DCAL;
dev_dbg(device_data->dev, "%s: after dcal -> DIN=0x%08x NBLW=%lu\n",
__func__, readl_relaxed(&device_data->base->din),
readl_relaxed(&device_data->base->str) & HASH_STR_NBLW_MASK);
while (readl(&device_data->base->str) & HASH_STR_DCAL_MASK)
cpu_relax();
}
/**
* hash_incrementlength - Increments the length of the current message.
* @ctx: Hash context
* @incr: Length of message processed already
*
* Overflow cannot occur, because conditions for overflow are checked in
* hash_hw_update.
*/
static void hash_incrementlength(struct hash_req_ctx *ctx, u32 incr)
{
ctx->state.length.low_word += incr;
/* Check for wrap-around */
if (ctx->state.length.low_word < incr)
ctx->state.length.high_word++;
}
/**
* hash_setconfiguration - Sets the required configuration for the hash
* hardware.
* @device_data: Structure for the hash device.
* @config: Pointer to a configuration structure.
*/
int hash_setconfiguration(struct hash_device_data *device_data,
struct hash_config *config)
{
int ret = 0;
if (config->algorithm != HASH_ALGO_SHA1 &&
config->algorithm != HASH_ALGO_SHA256)
return -EPERM;
/*
* DATAFORM bits. Set the DATAFORM bits to 0b11, which means the data
* to be written to HASH_DIN is considered as 32 bits.
*/
HASH_SET_DATA_FORMAT(config->data_format);
/*
* ALGO bit. Set to 0b1 for SHA-1 and 0b0 for SHA-256
*/
switch (config->algorithm) {
case HASH_ALGO_SHA1:
HASH_SET_BITS(&device_data->base->cr, HASH_CR_ALGO_MASK);
break;
case HASH_ALGO_SHA256:
HASH_CLEAR_BITS(&device_data->base->cr, HASH_CR_ALGO_MASK);
break;
default:
dev_err(device_data->dev, "%s: Incorrect algorithm\n",
__func__);
return -EPERM;
}
/*
* MODE bit. This bit selects between HASH or HMAC mode for the
* selected algorithm. 0b0 = HASH and 0b1 = HMAC.
*/
if (HASH_OPER_MODE_HASH == config->oper_mode)
HASH_CLEAR_BITS(&device_data->base->cr,
HASH_CR_MODE_MASK);
else if (HASH_OPER_MODE_HMAC == config->oper_mode) {
HASH_SET_BITS(&device_data->base->cr, HASH_CR_MODE_MASK);
if (device_data->current_ctx->keylen > HASH_BLOCK_SIZE) {
/* Truncate key to blocksize */
dev_dbg(device_data->dev, "%s: LKEY set\n", __func__);
HASH_SET_BITS(&device_data->base->cr,
HASH_CR_LKEY_MASK);
} else {
dev_dbg(device_data->dev, "%s: LKEY cleared\n",
__func__);
HASH_CLEAR_BITS(&device_data->base->cr,
HASH_CR_LKEY_MASK);
}
} else { /* Wrong hash mode */
ret = -EPERM;
dev_err(device_data->dev, "%s: HASH_INVALID_PARAMETER!\n",
__func__);
}
return ret;
}
/**
* hash_begin - This routine resets some globals and initializes the hash
* hardware.
* @device_data: Structure for the hash device.
* @ctx: Hash context.
*/
void hash_begin(struct hash_device_data *device_data, struct hash_ctx *ctx)
{
/* HW and SW initializations */
/* Note: there is no need to initialize buffer and digest members */
while (readl(&device_data->base->str) & HASH_STR_DCAL_MASK)
cpu_relax();
/*
* INIT bit. Set this bit to 0b1 to reset the HASH processor core and
* prepare the initialize the HASH accelerator to compute the message
* digest of a new message.
*/
HASH_INITIALIZE;
/*
* NBLW bits. Reset the number of bits in last word (NBLW).
*/
HASH_CLEAR_BITS(&device_data->base->str, HASH_STR_NBLW_MASK);
}
static int hash_process_data(struct hash_device_data *device_data,
struct hash_ctx *ctx, struct hash_req_ctx *req_ctx,
int msg_length, u8 *data_buffer, u8 *buffer,
u8 *index)
{
int ret = 0;
u32 count;
do {
if ((*index + msg_length) < HASH_BLOCK_SIZE) {
for (count = 0; count < msg_length; count++) {
buffer[*index + count] =
*(data_buffer + count);
}
*index += msg_length;
msg_length = 0;
} else {
if (req_ctx->updated) {
ret = hash_resume_state(device_data,
&device_data->state);
memmove(req_ctx->state.buffer,
device_data->state.buffer,
HASH_BLOCK_SIZE);
if (ret) {
dev_err(device_data->dev,
"%s: hash_resume_state() failed!\n",
__func__);
goto out;
}
} else {
ret = init_hash_hw(device_data, ctx);
if (ret) {
dev_err(device_data->dev,
"%s: init_hash_hw() failed!\n",
__func__);
goto out;
}
req_ctx->updated = 1;
}
/*
* If 'data_buffer' is four byte aligned and
* local buffer does not have any data, we can
* write data directly from 'data_buffer' to
* HW peripheral, otherwise we first copy data
* to a local buffer
*/
if ((0 == (((u32)data_buffer) % 4)) &&
(0 == *index))
hash_processblock(device_data,
(const u32 *)data_buffer,
HASH_BLOCK_SIZE);
else {
for (count = 0;
count < (u32)(HASH_BLOCK_SIZE - *index);
count++) {
buffer[*index + count] =
*(data_buffer + count);
}
hash_processblock(device_data,
(const u32 *)buffer,
HASH_BLOCK_SIZE);
}
hash_incrementlength(req_ctx, HASH_BLOCK_SIZE);
data_buffer += (HASH_BLOCK_SIZE - *index);
msg_length -= (HASH_BLOCK_SIZE - *index);
*index = 0;
ret = hash_save_state(device_data,
&device_data->state);
memmove(device_data->state.buffer,
req_ctx->state.buffer,
HASH_BLOCK_SIZE);
if (ret) {
dev_err(device_data->dev, "%s: hash_save_state() failed!\n",
__func__);
goto out;
}
}
} while (msg_length != 0);
out:
return ret;
}
/**
* hash_dma_final - The hash dma final function for SHA1/SHA256.
* @req: The hash request for the job.
*/
static int hash_dma_final(struct ahash_request *req)
{
int ret = 0;
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct hash_ctx *ctx = crypto_ahash_ctx(tfm);
struct hash_req_ctx *req_ctx = ahash_request_ctx(req);
struct hash_device_data *device_data;
u8 digest[SHA256_DIGEST_SIZE];
int bytes_written = 0;
ret = hash_get_device_data(ctx, &device_data);
if (ret)
return ret;
dev_dbg(device_data->dev, "%s: (ctx=0x%x)!\n", __func__, (u32) ctx);
if (req_ctx->updated) {
ret = hash_resume_state(device_data, &device_data->state);
if (ret) {
dev_err(device_data->dev, "%s: hash_resume_state() failed!\n",
__func__);
goto out;
}
}
if (!req_ctx->updated) {
ret = hash_setconfiguration(device_data, &ctx->config);
if (ret) {
dev_err(device_data->dev,
"%s: hash_setconfiguration() failed!\n",
__func__);
goto out;
}
/* Enable DMA input */
if (hash_mode != HASH_MODE_DMA || !req_ctx->dma_mode) {
HASH_CLEAR_BITS(&device_data->base->cr,
HASH_CR_DMAE_MASK);
} else {
HASH_SET_BITS(&device_data->base->cr,
HASH_CR_DMAE_MASK);
HASH_SET_BITS(&device_data->base->cr,
HASH_CR_PRIVN_MASK);
}
HASH_INITIALIZE;
if (ctx->config.oper_mode == HASH_OPER_MODE_HMAC)
hash_hw_write_key(device_data, ctx->key, ctx->keylen);
/* Number of bits in last word = (nbytes * 8) % 32 */
HASH_SET_NBLW((req->nbytes * 8) % 32);
req_ctx->updated = 1;
}
/* Store the nents in the dma struct. */
ctx->device->dma.nents = hash_get_nents(req->src, req->nbytes, NULL);
if (!ctx->device->dma.nents) {
dev_err(device_data->dev, "%s: ctx->device->dma.nents = 0\n",
__func__);
ret = ctx->device->dma.nents;
goto out;
}
bytes_written = hash_dma_write(ctx, req->src, req->nbytes);
if (bytes_written != req->nbytes) {
dev_err(device_data->dev, "%s: hash_dma_write() failed!\n",
__func__);
ret = bytes_written;
goto out;
}
wait_for_completion(&ctx->device->dma.complete);
hash_dma_done(ctx);
while (readl(&device_data->base->str) & HASH_STR_DCAL_MASK)
cpu_relax();
if (ctx->config.oper_mode == HASH_OPER_MODE_HMAC && ctx->key) {
unsigned int keylen = ctx->keylen;
u8 *key = ctx->key;
dev_dbg(device_data->dev, "%s: keylen: %d\n",
__func__, ctx->keylen);
hash_hw_write_key(device_data, key, keylen);
}
hash_get_digest(device_data, digest, ctx->config.algorithm);
memcpy(req->result, digest, ctx->digestsize);
out:
release_hash_device(device_data);
/**
* Allocated in setkey, and only used in HMAC.
*/
kfree(ctx->key);
return ret;
}
/**
* hash_hw_final - The final hash calculation function
* @req: The hash request for the job.
*/
static int hash_hw_final(struct ahash_request *req)
{
int ret = 0;
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct hash_ctx *ctx = crypto_ahash_ctx(tfm);
struct hash_req_ctx *req_ctx = ahash_request_ctx(req);
struct hash_device_data *device_data;
u8 digest[SHA256_DIGEST_SIZE];
ret = hash_get_device_data(ctx, &device_data);
if (ret)
return ret;
dev_dbg(device_data->dev, "%s: (ctx=0x%x)!\n", __func__, (u32) ctx);
if (req_ctx->updated) {
ret = hash_resume_state(device_data, &device_data->state);
if (ret) {
dev_err(device_data->dev,
"%s: hash_resume_state() failed!\n", __func__);
goto out;
}
} else if (req->nbytes == 0 && ctx->keylen == 0) {
u8 zero_hash[SHA256_DIGEST_SIZE];
u32 zero_hash_size = 0;
bool zero_digest = false;
/**
* Use a pre-calculated empty message digest
* (workaround since hw return zeroes, hw bug!?)
*/
ret = get_empty_message_digest(device_data, &zero_hash[0],
&zero_hash_size, &zero_digest);
if (!ret && likely(zero_hash_size == ctx->digestsize) &&
zero_digest) {
memcpy(req->result, &zero_hash[0], ctx->digestsize);
goto out;
} else if (!ret && !zero_digest) {
dev_dbg(device_data->dev,
"%s: HMAC zero msg with key, continue...\n",
__func__);
} else {
dev_err(device_data->dev,
"%s: ret=%d, or wrong digest size? %s\n",
__func__, ret,
zero_hash_size == ctx->digestsize ?
"true" : "false");
/* Return error */
goto out;
}
} else if (req->nbytes == 0 && ctx->keylen > 0) {
dev_err(device_data->dev, "%s: Empty message with keylength > 0, NOT supported\n",
__func__);
goto out;
}
if (!req_ctx->updated) {
ret = init_hash_hw(device_data, ctx);
if (ret) {
dev_err(device_data->dev,
"%s: init_hash_hw() failed!\n", __func__);
goto out;
}
}
if (req_ctx->state.index) {
hash_messagepad(device_data, req_ctx->state.buffer,
req_ctx->state.index);
} else {
HASH_SET_DCAL;
while (readl(&device_data->base->str) & HASH_STR_DCAL_MASK)
cpu_relax();
}
if (ctx->config.oper_mode == HASH_OPER_MODE_HMAC && ctx->key) {
unsigned int keylen = ctx->keylen;
u8 *key = ctx->key;
dev_dbg(device_data->dev, "%s: keylen: %d\n",
__func__, ctx->keylen);
hash_hw_write_key(device_data, key, keylen);
}
hash_get_digest(device_data, digest, ctx->config.algorithm);
memcpy(req->result, digest, ctx->digestsize);
out:
release_hash_device(device_data);
/**
* Allocated in setkey, and only used in HMAC.
*/
kfree(ctx->key);
return ret;
}
/**
* hash_hw_update - Updates current HASH computation hashing another part of
* the message.
* @req: Byte array containing the message to be hashed (caller
* allocated).
*/
int hash_hw_update(struct ahash_request *req)
{
int ret = 0;
u8 index = 0;
u8 *buffer;
struct hash_device_data *device_data;
u8 *data_buffer;
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct hash_ctx *ctx = crypto_ahash_ctx(tfm);
struct hash_req_ctx *req_ctx = ahash_request_ctx(req);
struct crypto_hash_walk walk;
int msg_length = crypto_hash_walk_first(req, &walk);
/* Empty message ("") is correct indata */
if (msg_length == 0)
return ret;
index = req_ctx->state.index;
buffer = (u8 *)req_ctx->state.buffer;
/* Check if ctx->state.length + msg_length
overflows */
if (msg_length > (req_ctx->state.length.low_word + msg_length) &&
HASH_HIGH_WORD_MAX_VAL == req_ctx->state.length.high_word) {
pr_err("%s: HASH_MSG_LENGTH_OVERFLOW!\n", __func__);
return -EPERM;
}
ret = hash_get_device_data(ctx, &device_data);
if (ret)
return ret;
/* Main loop */
while (0 != msg_length) {
data_buffer = walk.data;
ret = hash_process_data(device_data, ctx, req_ctx, msg_length,
data_buffer, buffer, &index);
if (ret) {
dev_err(device_data->dev, "%s: hash_internal_hw_update() failed!\n",
__func__);
goto out;
}
msg_length = crypto_hash_walk_done(&walk, 0);
}
req_ctx->state.index = index;
dev_dbg(device_data->dev, "%s: indata length=%d, bin=%d\n",
__func__, req_ctx->state.index, req_ctx->state.bit_index);
out:
release_hash_device(device_data);
return ret;
}
/**
* hash_resume_state - Function that resumes the state of an calculation.
* @device_data: Pointer to the device structure.
* @device_state: The state to be restored in the hash hardware
*/
int hash_resume_state(struct hash_device_data *device_data,
const struct hash_state *device_state)
{
u32 temp_cr;
s32 count;
int hash_mode = HASH_OPER_MODE_HASH;
if (NULL == device_state) {
dev_err(device_data->dev, "%s: HASH_INVALID_PARAMETER!\n",
__func__);
return -EPERM;
}
/* Check correctness of index and length members */
if (device_state->index > HASH_BLOCK_SIZE ||
(device_state->length.low_word % HASH_BLOCK_SIZE) != 0) {
dev_err(device_data->dev, "%s: HASH_INVALID_PARAMETER!\n",
__func__);
return -EPERM;
}
/*
* INIT bit. Set this bit to 0b1 to reset the HASH processor core and
* prepare the initialize the HASH accelerator to compute the message
* digest of a new message.
*/
HASH_INITIALIZE;
temp_cr = device_state->temp_cr;
writel_relaxed(temp_cr & HASH_CR_RESUME_MASK, &device_data->base->cr);
if (readl(&device_data->base->cr) & HASH_CR_MODE_MASK)
hash_mode = HASH_OPER_MODE_HMAC;
else
hash_mode = HASH_OPER_MODE_HASH;
for (count = 0; count < HASH_CSR_COUNT; count++) {
if ((count >= 36) && (hash_mode == HASH_OPER_MODE_HASH))
break;
writel_relaxed(device_state->csr[count],
&device_data->base->csrx[count]);
}
writel_relaxed(device_state->csfull, &device_data->base->csfull);
writel_relaxed(device_state->csdatain, &device_data->base->csdatain);
writel_relaxed(device_state->str_reg, &device_data->base->str);
writel_relaxed(temp_cr, &device_data->base->cr);
return 0;
}
/**
* hash_save_state - Function that saves the state of hardware.
* @device_data: Pointer to the device structure.
* @device_state: The strucure where the hardware state should be saved.
*/
int hash_save_state(struct hash_device_data *device_data,
struct hash_state *device_state)
{
u32 temp_cr;
u32 count;
int hash_mode = HASH_OPER_MODE_HASH;
if (NULL == device_state) {
dev_err(device_data->dev, "%s: HASH_INVALID_PARAMETER!\n",
__func__);
return -ENOTSUPP;
}
/* Write dummy value to force digest intermediate calculation. This
* actually makes sure that there isn't any ongoing calculation in the
* hardware.
*/
while (readl(&device_data->base->str) & HASH_STR_DCAL_MASK)
cpu_relax();
temp_cr = readl_relaxed(&device_data->base->cr);
device_state->str_reg = readl_relaxed(&device_data->base->str);
device_state->din_reg = readl_relaxed(&device_data->base->din);
if (readl(&device_data->base->cr) & HASH_CR_MODE_MASK)
hash_mode = HASH_OPER_MODE_HMAC;
else
hash_mode = HASH_OPER_MODE_HASH;
for (count = 0; count < HASH_CSR_COUNT; count++) {
if ((count >= 36) && (hash_mode == HASH_OPER_MODE_HASH))
break;
device_state->csr[count] =
readl_relaxed(&device_data->base->csrx[count]);
}
device_state->csfull = readl_relaxed(&device_data->base->csfull);
device_state->csdatain = readl_relaxed(&device_data->base->csdatain);
device_state->temp_cr = temp_cr;
return 0;
}
/**
* hash_check_hw - This routine checks for peripheral Ids and PCell Ids.
* @device_data:
*
*/
int hash_check_hw(struct hash_device_data *device_data)
{
/* Checking Peripheral Ids */
if (HASH_P_ID0 == readl_relaxed(&device_data->base->periphid0) &&
HASH_P_ID1 == readl_relaxed(&device_data->base->periphid1) &&
HASH_P_ID2 == readl_relaxed(&device_data->base->periphid2) &&
HASH_P_ID3 == readl_relaxed(&device_data->base->periphid3) &&
HASH_CELL_ID0 == readl_relaxed(&device_data->base->cellid0) &&
HASH_CELL_ID1 == readl_relaxed(&device_data->base->cellid1) &&
HASH_CELL_ID2 == readl_relaxed(&device_data->base->cellid2) &&
HASH_CELL_ID3 == readl_relaxed(&device_data->base->cellid3)) {
return 0;
}
dev_err(device_data->dev, "%s: HASH_UNSUPPORTED_HW!\n", __func__);
return -ENOTSUPP;
}
/**
* hash_get_digest - Gets the digest.
* @device_data: Pointer to the device structure.
* @digest: User allocated byte array for the calculated digest.
* @algorithm: The algorithm in use.
*/
void hash_get_digest(struct hash_device_data *device_data,
u8 *digest, int algorithm)
{
u32 temp_hx_val, count;
int loop_ctr;
if (algorithm != HASH_ALGO_SHA1 && algorithm != HASH_ALGO_SHA256) {
dev_err(device_data->dev, "%s: Incorrect algorithm %d\n",
__func__, algorithm);
return;
}
if (algorithm == HASH_ALGO_SHA1)
loop_ctr = SHA1_DIGEST_SIZE / sizeof(u32);
else
loop_ctr = SHA256_DIGEST_SIZE / sizeof(u32);
dev_dbg(device_data->dev, "%s: digest array:(0x%x)\n",
__func__, (u32) digest);
/* Copy result into digest array */
for (count = 0; count < loop_ctr; count++) {
temp_hx_val = readl_relaxed(&device_data->base->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);
}
}
/**
* hash_update - The hash update function for SHA1/SHA2 (SHA256).
* @req: The hash request for the job.
*/
static int ahash_update(struct ahash_request *req)
{
int ret = 0;
struct hash_req_ctx *req_ctx = ahash_request_ctx(req);
if (hash_mode != HASH_MODE_DMA || !req_ctx->dma_mode)
ret = hash_hw_update(req);
/* Skip update for DMA, all data will be passed to DMA in final */
if (ret) {
pr_err("%s: hash_hw_update() failed!\n", __func__);
}
return ret;
}
/**
* hash_final - The hash final function for SHA1/SHA2 (SHA256).
* @req: The hash request for the job.
*/
static int ahash_final(struct ahash_request *req)
{
int ret = 0;
struct hash_req_ctx *req_ctx = ahash_request_ctx(req);
pr_debug("%s: data size: %d\n", __func__, req->nbytes);
if ((hash_mode == HASH_MODE_DMA) && req_ctx->dma_mode)
ret = hash_dma_final(req);
else
ret = hash_hw_final(req);
if (ret) {
pr_err("%s: hash_hw/dma_final() failed\n", __func__);
}
return ret;
}
static int hash_setkey(struct crypto_ahash *tfm,
const u8 *key, unsigned int keylen, int alg)
{
int ret = 0;
struct hash_ctx *ctx = crypto_ahash_ctx(tfm);
/**
* Freed in final.
*/
ctx->key = kmemdup(key, keylen, GFP_KERNEL);
if (!ctx->key) {
pr_err("%s: Failed to allocate ctx->key for %d\n",
__func__, alg);
return -ENOMEM;
}
ctx->keylen = keylen;
return ret;
}
static int ahash_sha1_init(struct ahash_request *req)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct hash_ctx *ctx = crypto_ahash_ctx(tfm);
ctx->config.data_format = HASH_DATA_8_BITS;
ctx->config.algorithm = HASH_ALGO_SHA1;
ctx->config.oper_mode = HASH_OPER_MODE_HASH;
ctx->digestsize = SHA1_DIGEST_SIZE;
return hash_init(req);
}
static int ahash_sha256_init(struct ahash_request *req)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct hash_ctx *ctx = crypto_ahash_ctx(tfm);
ctx->config.data_format = HASH_DATA_8_BITS;
ctx->config.algorithm = HASH_ALGO_SHA256;
ctx->config.oper_mode = HASH_OPER_MODE_HASH;
ctx->digestsize = SHA256_DIGEST_SIZE;
return hash_init(req);
}
static int ahash_sha1_digest(struct ahash_request *req)
{
int ret2, ret1;
ret1 = ahash_sha1_init(req);
if (ret1)
goto out;
ret1 = ahash_update(req);
ret2 = ahash_final(req);
out:
return ret1 ? ret1 : ret2;
}
static int ahash_sha256_digest(struct ahash_request *req)
{
int ret2, ret1;
ret1 = ahash_sha256_init(req);
if (ret1)
goto out;
ret1 = ahash_update(req);
ret2 = ahash_final(req);
out:
return ret1 ? ret1 : ret2;
}
static int ahash_noimport(struct ahash_request *req, const void *in)
{
return -ENOSYS;
}
static int ahash_noexport(struct ahash_request *req, void *out)
{
return -ENOSYS;
}
static int hmac_sha1_init(struct ahash_request *req)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct hash_ctx *ctx = crypto_ahash_ctx(tfm);
ctx->config.data_format = HASH_DATA_8_BITS;
ctx->config.algorithm = HASH_ALGO_SHA1;
ctx->config.oper_mode = HASH_OPER_MODE_HMAC;
ctx->digestsize = SHA1_DIGEST_SIZE;
return hash_init(req);
}
static int hmac_sha256_init(struct ahash_request *req)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct hash_ctx *ctx = crypto_ahash_ctx(tfm);
ctx->config.data_format = HASH_DATA_8_BITS;
ctx->config.algorithm = HASH_ALGO_SHA256;
ctx->config.oper_mode = HASH_OPER_MODE_HMAC;
ctx->digestsize = SHA256_DIGEST_SIZE;
return hash_init(req);
}
static int hmac_sha1_digest(struct ahash_request *req)
{
int ret2, ret1;
ret1 = hmac_sha1_init(req);
if (ret1)
goto out;
ret1 = ahash_update(req);
ret2 = ahash_final(req);
out:
return ret1 ? ret1 : ret2;
}
static int hmac_sha256_digest(struct ahash_request *req)
{
int ret2, ret1;
ret1 = hmac_sha256_init(req);
if (ret1)
goto out;
ret1 = ahash_update(req);
ret2 = ahash_final(req);
out:
return ret1 ? ret1 : ret2;
}
static int hmac_sha1_setkey(struct crypto_ahash *tfm,
const u8 *key, unsigned int keylen)
{
return hash_setkey(tfm, key, keylen, HASH_ALGO_SHA1);
}
static int hmac_sha256_setkey(struct crypto_ahash *tfm,
const u8 *key, unsigned int keylen)
{
return hash_setkey(tfm, key, keylen, HASH_ALGO_SHA256);
}
struct hash_algo_template {
struct hash_config conf;
struct ahash_alg hash;
};
static int hash_cra_init(struct crypto_tfm *tfm)
{
struct hash_ctx *ctx = crypto_tfm_ctx(tfm);
struct crypto_alg *alg = tfm->__crt_alg;
struct hash_algo_template *hash_alg;
hash_alg = container_of(__crypto_ahash_alg(alg),
struct hash_algo_template,
hash);
crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
sizeof(struct hash_req_ctx));
ctx->config.data_format = HASH_DATA_8_BITS;
ctx->config.algorithm = hash_alg->conf.algorithm;
ctx->config.oper_mode = hash_alg->conf.oper_mode;
ctx->digestsize = hash_alg->hash.halg.digestsize;
return 0;
}
static struct hash_algo_template hash_algs[] = {
{
.conf.algorithm = HASH_ALGO_SHA1,
.conf.oper_mode = HASH_OPER_MODE_HASH,
.hash = {
.init = hash_init,
.update = ahash_update,
.final = ahash_final,
.digest = ahash_sha1_digest,
.export = ahash_noexport,
.import = ahash_noimport,
.halg.digestsize = SHA1_DIGEST_SIZE,
.halg.statesize = sizeof(struct hash_ctx),
.halg.base = {
.cra_name = "sha1",
.cra_driver_name = "sha1-ux500",
.cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = SHA1_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct hash_ctx),
.cra_init = hash_cra_init,
.cra_module = THIS_MODULE,
}
}
},
{
.conf.algorithm = HASH_ALGO_SHA256,
.conf.oper_mode = HASH_OPER_MODE_HASH,
.hash = {
.init = hash_init,
.update = ahash_update,
.final = ahash_final,
.digest = ahash_sha256_digest,
.export = ahash_noexport,
.import = ahash_noimport,
.halg.digestsize = SHA256_DIGEST_SIZE,
.halg.statesize = sizeof(struct hash_ctx),
.halg.base = {
.cra_name = "sha256",
.cra_driver_name = "sha256-ux500",
.cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = SHA256_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct hash_ctx),
.cra_init = hash_cra_init,
.cra_module = THIS_MODULE,
}
}
},
{
.conf.algorithm = HASH_ALGO_SHA1,
.conf.oper_mode = HASH_OPER_MODE_HMAC,
.hash = {
.init = hash_init,
.update = ahash_update,
.final = ahash_final,
.digest = hmac_sha1_digest,
.setkey = hmac_sha1_setkey,
.export = ahash_noexport,
.import = ahash_noimport,
.halg.digestsize = SHA1_DIGEST_SIZE,
.halg.statesize = sizeof(struct hash_ctx),
.halg.base = {
.cra_name = "hmac(sha1)",
.cra_driver_name = "hmac-sha1-ux500",
.cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = SHA1_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct hash_ctx),
.cra_init = hash_cra_init,
.cra_module = THIS_MODULE,
}
}
},
{
.conf.algorithm = HASH_ALGO_SHA256,
.conf.oper_mode = HASH_OPER_MODE_HMAC,
.hash = {
.init = hash_init,
.update = ahash_update,
.final = ahash_final,
.digest = hmac_sha256_digest,
.setkey = hmac_sha256_setkey,
.export = ahash_noexport,
.import = ahash_noimport,
.halg.digestsize = SHA256_DIGEST_SIZE,
.halg.statesize = sizeof(struct hash_ctx),
.halg.base = {
.cra_name = "hmac(sha256)",
.cra_driver_name = "hmac-sha256-ux500",
.cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = SHA256_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct hash_ctx),
.cra_init = hash_cra_init,
.cra_module = THIS_MODULE,
}
}
}
};
/**
* hash_algs_register_all -
*/
static int ahash_algs_register_all(struct hash_device_data *device_data)
{
int ret;
int i;
int count;
for (i = 0; i < ARRAY_SIZE(hash_algs); i++) {
ret = crypto_register_ahash(&hash_algs[i].hash);
if (ret) {
count = i;
dev_err(device_data->dev, "%s: alg registration failed\n",
hash_algs[i].hash.halg.base.cra_driver_name);
goto unreg;
}
}
return 0;
unreg:
for (i = 0; i < count; i++)
crypto_unregister_ahash(&hash_algs[i].hash);
return ret;
}
/**
* hash_algs_unregister_all -
*/
static void ahash_algs_unregister_all(struct hash_device_data *device_data)
{
int i;
for (i = 0; i < ARRAY_SIZE(hash_algs); i++)
crypto_unregister_ahash(&hash_algs[i].hash);
}
/**
* ux500_hash_probe - Function that probes the hash hardware.
* @pdev: The platform device.
*/
static int ux500_hash_probe(struct platform_device *pdev)
{
int ret = 0;
struct resource *res = NULL;
struct hash_device_data *device_data;
struct device *dev = &pdev->dev;
device_data = devm_kzalloc(dev, sizeof(*device_data), GFP_ATOMIC);
if (!device_data) {
ret = -ENOMEM;
goto out;
}
device_data->dev = dev;
device_data->current_ctx = NULL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_dbg(dev, "%s: platform_get_resource() failed!\n", __func__);
ret = -ENODEV;
goto out;
}
device_data->phybase = res->start;
device_data->base = devm_ioremap_resource(dev, res);
if (IS_ERR(device_data->base)) {
dev_err(dev, "%s: ioremap() failed!\n", __func__);
ret = PTR_ERR(device_data->base);
goto out;
}
spin_lock_init(&device_data->ctx_lock);
spin_lock_init(&device_data->power_state_lock);
/* Enable power for HASH1 hardware block */
device_data->regulator = regulator_get(dev, "v-ape");
if (IS_ERR(device_data->regulator)) {
dev_err(dev, "%s: regulator_get() failed!\n", __func__);
ret = PTR_ERR(device_data->regulator);
device_data->regulator = NULL;
goto out;
}
/* Enable the clock for HASH1 hardware block */
device_data->clk = devm_clk_get(dev, NULL);
if (IS_ERR(device_data->clk)) {
dev_err(dev, "%s: clk_get() failed!\n", __func__);
ret = PTR_ERR(device_data->clk);
goto out_regulator;
}
ret = clk_prepare(device_data->clk);
if (ret) {
dev_err(dev, "%s: clk_prepare() failed!\n", __func__);
goto out_regulator;
}
/* Enable device power (and clock) */
ret = hash_enable_power(device_data, false);
if (ret) {
dev_err(dev, "%s: hash_enable_power() failed!\n", __func__);
goto out_clk_unprepare;
}
ret = hash_check_hw(device_data);
if (ret) {
dev_err(dev, "%s: hash_check_hw() failed!\n", __func__);
goto out_power;
}
if (hash_mode == HASH_MODE_DMA)
hash_dma_setup_channel(device_data, dev);
platform_set_drvdata(pdev, device_data);
/* Put the new device into the device list... */
klist_add_tail(&device_data->list_node, &driver_data.device_list);
/* ... and signal that a new device is available. */
up(&driver_data.device_allocation);
ret = ahash_algs_register_all(device_data);
if (ret) {
dev_err(dev, "%s: ahash_algs_register_all() failed!\n",
__func__);
goto out_power;
}
dev_info(dev, "successfully registered\n");
return 0;
out_power:
hash_disable_power(device_data, false);
out_clk_unprepare:
clk_unprepare(device_data->clk);
out_regulator:
regulator_put(device_data->regulator);
out:
return ret;
}
/**
* ux500_hash_remove - Function that removes the hash device from the platform.
* @pdev: The platform device.
*/
static int ux500_hash_remove(struct platform_device *pdev)
{
struct hash_device_data *device_data;
struct device *dev = &pdev->dev;
device_data = platform_get_drvdata(pdev);
if (!device_data) {
dev_err(dev, "%s: platform_get_drvdata() failed!\n", __func__);
return -ENOMEM;
}
/* Try to decrease the number of available devices. */
if (down_trylock(&driver_data.device_allocation))
return -EBUSY;
/* Check that the device is free */
spin_lock(&device_data->ctx_lock);
/* current_ctx allocates a device, NULL = unallocated */
if (device_data->current_ctx) {
/* The device is busy */
spin_unlock(&device_data->ctx_lock);
/* Return the device to the pool. */
up(&driver_data.device_allocation);
return -EBUSY;
}
spin_unlock(&device_data->ctx_lock);
/* Remove the device from the list */
if (klist_node_attached(&device_data->list_node))
klist_remove(&device_data->list_node);
/* If this was the last device, remove the services */
if (list_empty(&driver_data.device_list.k_list))
ahash_algs_unregister_all(device_data);
if (hash_disable_power(device_data, false))
dev_err(dev, "%s: hash_disable_power() failed\n",
__func__);
clk_unprepare(device_data->clk);
regulator_put(device_data->regulator);
return 0;
}
/**
* ux500_hash_shutdown - Function that shutdown the hash device.
* @pdev: The platform device
*/
static void ux500_hash_shutdown(struct platform_device *pdev)
{
struct hash_device_data *device_data;
device_data = platform_get_drvdata(pdev);
if (!device_data) {
dev_err(&pdev->dev, "%s: platform_get_drvdata() failed!\n",
__func__);
return;
}
/* Check that the device is free */
spin_lock(&device_data->ctx_lock);
/* current_ctx allocates a device, NULL = unallocated */
if (!device_data->current_ctx) {
if (down_trylock(&driver_data.device_allocation))
dev_dbg(&pdev->dev, "%s: Cryp still in use! Shutting down anyway...\n",
__func__);
/**
* (Allocate the device)
* Need to set this to non-null (dummy) value,
* to avoid usage if context switching.
*/
device_data->current_ctx++;
}
spin_unlock(&device_data->ctx_lock);
/* Remove the device from the list */
if (klist_node_attached(&device_data->list_node))
klist_remove(&device_data->list_node);
/* If this was the last device, remove the services */
if (list_empty(&driver_data.device_list.k_list))
ahash_algs_unregister_all(device_data);
if (hash_disable_power(device_data, false))
dev_err(&pdev->dev, "%s: hash_disable_power() failed\n",
__func__);
}
#ifdef CONFIG_PM_SLEEP
/**
* ux500_hash_suspend - Function that suspends the hash device.
* @dev: Device to suspend.
*/
static int ux500_hash_suspend(struct device *dev)
{
int ret;
struct hash_device_data *device_data;
struct hash_ctx *temp_ctx = NULL;
device_data = dev_get_drvdata(dev);
if (!device_data) {
dev_err(dev, "%s: platform_get_drvdata() failed!\n", __func__);
return -ENOMEM;
}
spin_lock(&device_data->ctx_lock);
if (!device_data->current_ctx)
device_data->current_ctx++;
spin_unlock(&device_data->ctx_lock);
if (device_data->current_ctx == ++temp_ctx) {
if (down_interruptible(&driver_data.device_allocation))
dev_dbg(dev, "%s: down_interruptible() failed\n",
__func__);
ret = hash_disable_power(device_data, false);
} else {
ret = hash_disable_power(device_data, true);
}
if (ret)
dev_err(dev, "%s: hash_disable_power()\n", __func__);
return ret;
}
/**
* ux500_hash_resume - Function that resume the hash device.
* @dev: Device to resume.
*/
static int ux500_hash_resume(struct device *dev)
{
int ret = 0;
struct hash_device_data *device_data;
struct hash_ctx *temp_ctx = NULL;
device_data = dev_get_drvdata(dev);
if (!device_data) {
dev_err(dev, "%s: platform_get_drvdata() failed!\n", __func__);
return -ENOMEM;
}
spin_lock(&device_data->ctx_lock);
if (device_data->current_ctx == ++temp_ctx)
device_data->current_ctx = NULL;
spin_unlock(&device_data->ctx_lock);
if (!device_data->current_ctx)
up(&driver_data.device_allocation);
else
ret = hash_enable_power(device_data, true);
if (ret)
dev_err(dev, "%s: hash_enable_power() failed!\n", __func__);
return ret;
}
#endif
static SIMPLE_DEV_PM_OPS(ux500_hash_pm, ux500_hash_suspend, ux500_hash_resume);
static const struct of_device_id ux500_hash_match[] = {
{ .compatible = "stericsson,ux500-hash" },
{ },
};
MODULE_DEVICE_TABLE(of, ux500_hash_match);
static struct platform_driver hash_driver = {
.probe = ux500_hash_probe,
.remove = ux500_hash_remove,
.shutdown = ux500_hash_shutdown,
.driver = {
.name = "hash1",
.of_match_table = ux500_hash_match,
.pm = &ux500_hash_pm,
}
};
/**
* ux500_hash_mod_init - The kernel module init function.
*/
static int __init ux500_hash_mod_init(void)
{
klist_init(&driver_data.device_list, NULL, NULL);
/* Initialize the semaphore to 0 devices (locked state) */
sema_init(&driver_data.device_allocation, 0);
return platform_driver_register(&hash_driver);
}
/**
* ux500_hash_mod_fini - The kernel module exit function.
*/
static void __exit ux500_hash_mod_fini(void)
{
platform_driver_unregister(&hash_driver);
}
module_init(ux500_hash_mod_init);
module_exit(ux500_hash_mod_fini);
MODULE_DESCRIPTION("Driver for ST-Ericsson UX500 HASH engine.");
MODULE_LICENSE("GPL");
MODULE_ALIAS_CRYPTO("sha1-all");
MODULE_ALIAS_CRYPTO("sha256-all");
MODULE_ALIAS_CRYPTO("hmac-sha1-all");
MODULE_ALIAS_CRYPTO("hmac-sha256-all");