Logo Search packages:      
Sourcecode: linux version File versions  Download package

padlock-sha.c

/*
 * Cryptographic API.
 *
 * Support for VIA PadLock hardware crypto engine.
 *
 * Copyright (c) 2006  Michal Ludvig <michal@logix.cz>
 *
 * 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 <crypto/algapi.h>
#include <crypto/sha.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/cryptohash.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/scatterlist.h>
#include "padlock.h"

#define SHA1_DEFAULT_FALLBACK "sha1-generic"
#define SHA256_DEFAULT_FALLBACK "sha256-generic"

struct padlock_sha_ctx {
      char        *data;
      size_t            used;
      int         bypass;
      void (*f_sha_padlock)(const char *in, char *out, int count);
      struct hash_desc fallback;
};

static inline struct padlock_sha_ctx *ctx(struct crypto_tfm *tfm)
{
      return crypto_tfm_ctx(tfm);
}

/* We'll need aligned address on the stack */
#define NEAREST_ALIGNED(ptr) \
      ((void *)ALIGN((size_t)(ptr), PADLOCK_ALIGNMENT))

static struct crypto_alg sha1_alg, sha256_alg;

static void padlock_sha_bypass(struct crypto_tfm *tfm)
{
      if (ctx(tfm)->bypass)
            return;

      crypto_hash_init(&ctx(tfm)->fallback);
      if (ctx(tfm)->data && ctx(tfm)->used) {
            struct scatterlist sg;

            sg_init_one(&sg, ctx(tfm)->data, ctx(tfm)->used);
            crypto_hash_update(&ctx(tfm)->fallback, &sg, sg.length);
      }

      ctx(tfm)->used = 0;
      ctx(tfm)->bypass = 1;
}

static void padlock_sha_init(struct crypto_tfm *tfm)
{
      ctx(tfm)->used = 0;
      ctx(tfm)->bypass = 0;
}

static void padlock_sha_update(struct crypto_tfm *tfm,
                  const uint8_t *data, unsigned int length)
{
      /* Our buffer is always one page. */
      if (unlikely(!ctx(tfm)->bypass &&
                 (ctx(tfm)->used + length > PAGE_SIZE)))
            padlock_sha_bypass(tfm);

      if (unlikely(ctx(tfm)->bypass)) {
            struct scatterlist sg;
            sg_init_one(&sg, (uint8_t *)data, length);
            crypto_hash_update(&ctx(tfm)->fallback, &sg, length);
            return;
      }

      memcpy(ctx(tfm)->data + ctx(tfm)->used, data, length);
      ctx(tfm)->used += length;
}

static inline void padlock_output_block(uint32_t *src,
                  uint32_t *dst, size_t count)
{
      while (count--)
            *dst++ = swab32(*src++);
}

static void padlock_do_sha1(const char *in, char *out, int count)
{
      /* We can't store directly to *out as it may be unaligned. */
      /* BTW Don't reduce the buffer size below 128 Bytes!
       *     PadLock microcode needs it that big. */
      char buf[128+16];
      char *result = NEAREST_ALIGNED(buf);

      ((uint32_t *)result)[0] = SHA1_H0;
      ((uint32_t *)result)[1] = SHA1_H1;
      ((uint32_t *)result)[2] = SHA1_H2;
      ((uint32_t *)result)[3] = SHA1_H3;
      ((uint32_t *)result)[4] = SHA1_H4;
 
      asm volatile (".byte 0xf3,0x0f,0xa6,0xc8" /* rep xsha1 */
                  : "+S"(in), "+D"(result)
                  : "c"(count), "a"(0));

      padlock_output_block((uint32_t *)result, (uint32_t *)out, 5);
}

static void padlock_do_sha256(const char *in, char *out, int count)
{
      /* We can't store directly to *out as it may be unaligned. */
      /* BTW Don't reduce the buffer size below 128 Bytes!
       *     PadLock microcode needs it that big. */
      char buf[128+16];
      char *result = NEAREST_ALIGNED(buf);

      ((uint32_t *)result)[0] = SHA256_H0;
      ((uint32_t *)result)[1] = SHA256_H1;
      ((uint32_t *)result)[2] = SHA256_H2;
      ((uint32_t *)result)[3] = SHA256_H3;
      ((uint32_t *)result)[4] = SHA256_H4;
      ((uint32_t *)result)[5] = SHA256_H5;
      ((uint32_t *)result)[6] = SHA256_H6;
      ((uint32_t *)result)[7] = SHA256_H7;

      asm volatile (".byte 0xf3,0x0f,0xa6,0xd0" /* rep xsha256 */
                  : "+S"(in), "+D"(result)
                  : "c"(count), "a"(0));

      padlock_output_block((uint32_t *)result, (uint32_t *)out, 8);
}

static void padlock_sha_final(struct crypto_tfm *tfm, uint8_t *out)
{
      if (unlikely(ctx(tfm)->bypass)) {
            crypto_hash_final(&ctx(tfm)->fallback, out);
            ctx(tfm)->bypass = 0;
            return;
      }

      /* Pass the input buffer to PadLock microcode... */
      ctx(tfm)->f_sha_padlock(ctx(tfm)->data, out, ctx(tfm)->used);

      ctx(tfm)->used = 0;
}

static int padlock_cra_init(struct crypto_tfm *tfm)
{
      const char *fallback_driver_name = tfm->__crt_alg->cra_name;
      struct crypto_hash *fallback_tfm;

      /* For now we'll allocate one page. This
       * could eventually be configurable one day. */
      ctx(tfm)->data = (char *)__get_free_page(GFP_KERNEL);
      if (!ctx(tfm)->data)
            return -ENOMEM;

      /* Allocate a fallback and abort if it failed. */
      fallback_tfm = crypto_alloc_hash(fallback_driver_name, 0,
                               CRYPTO_ALG_ASYNC |
                               CRYPTO_ALG_NEED_FALLBACK);
      if (IS_ERR(fallback_tfm)) {
            printk(KERN_WARNING PFX "Fallback driver '%s' could not be loaded!\n",
                   fallback_driver_name);
            free_page((unsigned long)(ctx(tfm)->data));
            return PTR_ERR(fallback_tfm);
      }

      ctx(tfm)->fallback.tfm = fallback_tfm;
      return 0;
}

static int padlock_sha1_cra_init(struct crypto_tfm *tfm)
{
      ctx(tfm)->f_sha_padlock = padlock_do_sha1;

      return padlock_cra_init(tfm);
}

static int padlock_sha256_cra_init(struct crypto_tfm *tfm)
{
      ctx(tfm)->f_sha_padlock = padlock_do_sha256;

      return padlock_cra_init(tfm);
}

static void padlock_cra_exit(struct crypto_tfm *tfm)
{
      if (ctx(tfm)->data) {
            free_page((unsigned long)(ctx(tfm)->data));
            ctx(tfm)->data = NULL;
      }

      crypto_free_hash(ctx(tfm)->fallback.tfm);
      ctx(tfm)->fallback.tfm = NULL;
}

static struct crypto_alg sha1_alg = {
      .cra_name         =     "sha1",
      .cra_driver_name  =     "sha1-padlock",
      .cra_priority           =     PADLOCK_CRA_PRIORITY,
      .cra_flags        =     CRYPTO_ALG_TYPE_DIGEST |
                              CRYPTO_ALG_NEED_FALLBACK,
      .cra_blocksize          =     SHA1_BLOCK_SIZE,
      .cra_ctxsize            =     sizeof(struct padlock_sha_ctx),
      .cra_module       =     THIS_MODULE,
      .cra_list         =     LIST_HEAD_INIT(sha1_alg.cra_list),
      .cra_init         =     padlock_sha1_cra_init,
      .cra_exit         =     padlock_cra_exit,
      .cra_u                  =     {
            .digest = {
                  .dia_digestsize   =     SHA1_DIGEST_SIZE,
                  .dia_init         =     padlock_sha_init,
                  .dia_update       =     padlock_sha_update,
                  .dia_final        =     padlock_sha_final,
            }
      }
};

static struct crypto_alg sha256_alg = {
      .cra_name         =     "sha256",
      .cra_driver_name  =     "sha256-padlock",
      .cra_priority           =     PADLOCK_CRA_PRIORITY,
      .cra_flags        =     CRYPTO_ALG_TYPE_DIGEST |
                              CRYPTO_ALG_NEED_FALLBACK,
      .cra_blocksize          =     SHA256_BLOCK_SIZE,
      .cra_ctxsize            =     sizeof(struct padlock_sha_ctx),
      .cra_module       =     THIS_MODULE,
      .cra_list         =     LIST_HEAD_INIT(sha256_alg.cra_list),
      .cra_init         =     padlock_sha256_cra_init,
      .cra_exit         =     padlock_cra_exit,
      .cra_u                  =     {
            .digest = {
                  .dia_digestsize   =     SHA256_DIGEST_SIZE,
                  .dia_init         =     padlock_sha_init,
                  .dia_update       =     padlock_sha_update,
                  .dia_final        =     padlock_sha_final,
            }
      }
};

static int __init padlock_init(void)
{
      int rc = -ENODEV;

      if (!cpu_has_phe) {
            printk(KERN_ERR PFX "VIA PadLock Hash Engine not detected.\n");
            return -ENODEV;
      }

      if (!cpu_has_phe_enabled) {
            printk(KERN_ERR PFX "VIA PadLock detected, but not enabled. Hmm, strange...\n");
            return -ENODEV;
      }

      rc = crypto_register_alg(&sha1_alg);
      if (rc)
            goto out;

      rc = crypto_register_alg(&sha256_alg);
      if (rc)
            goto out_unreg1;

      printk(KERN_NOTICE PFX "Using VIA PadLock ACE for SHA1/SHA256 algorithms.\n");

      return 0;

out_unreg1:
      crypto_unregister_alg(&sha1_alg);
out:
      printk(KERN_ERR PFX "VIA PadLock SHA1/SHA256 initialization failed.\n");
      return rc;
}

static void __exit padlock_fini(void)
{
      crypto_unregister_alg(&sha1_alg);
      crypto_unregister_alg(&sha256_alg);
}

module_init(padlock_init);
module_exit(padlock_fini);

MODULE_DESCRIPTION("VIA PadLock SHA1/SHA256 algorithms support.");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michal Ludvig");

MODULE_ALIAS("sha1");
MODULE_ALIAS("sha256");
MODULE_ALIAS("sha1-padlock");
MODULE_ALIAS("sha256-padlock");

Generated by  Doxygen 1.6.0   Back to index