Herbert Xu wrote: > Hi Michal: > > On Mon, May 22, 2006 at 02:15:07PM +1200, Michal Ludvig wrote: >> we should be able to autoload modules via their cra_driver_name, not >> only by the module name. I added a few MODULE_ALIAS()es, for now only >> for aes.ko which sets its driver-name and for SHA1/256 modules that I >> need as fallbacks for padlock-sha. > > I don't see anything wrong with the aliases. However, I doubt this is > what you really want for padlock-sha. > > After all, what you need is a SHA fallback, not this one in particular. > For instance, if we ever have an assembly version of SHA, you want that > one instead of sha1-generic.
That's configurable with module parameters. Defaults to sha1-generic but you can easily add sha1_fallback=sha1-i586 to your modprobe command. To use padlock or sha1-i586 by default you'll have to edit /etc/modprobe.conf anyway so it's not a big deal to add some parameters as well. > So you want to find it as something like a "sha1" algorithm with priority > less than your own. I've got a patch that does this (attached fyi) but I reverted it from my tree in favor of specifying the fallback module by name. > BTW, for the >1 page case you could use vmap instead of falling back. Hm, yes. Anyway - what if the nasty kernel tried to hash _really_ lots of data? The fallback option is cheap - I don't think it will be used much. I'll do some observations on how much data is usually hashed - I guess both IPsec on ethernet and dm-crypt will fit into one page, maybe IPsec with jumbo frames could use up to three pages. This can be configurable as a parameter as well (buffer_pages=NNN). Is anything else likely to use larger buffers? I believe the fallback path will be there as a "just-in-case" option. Attached are two patches - one that allows selecting crypto_alg by priority and then the PadLock-sha which doesn't actually use the priority stuff but instead the driver names for selecting fallback ;-) BTW The HW parts of padlock are still missing so you can apply it, unselect padlock-aes and compile and test (e.g. with tcrypt) on any x86 machine. Michal
Index: linux/crypto/api.c =================================================================== --- linux.orig/crypto/api.c +++ linux/crypto/api.c @@ -39,7 +39,28 @@ static inline void crypto_alg_put(struct module_put(alg->cra_module); } -static struct crypto_alg *crypto_alg_lookup(const char *name) +static inline int prio_compare(int req, int best, int cur, + enum prio_flags flags) +{ + switch (flags) { + case CRA_PRIO_LOWEST: + return (cur < best); + case CRA_PRIO_LOWER_THAN: + return (cur < req && cur > best); + case CRA_PRIO_EXACT: + return (cur == req); + case CRA_PRIO_HIGHER_THAN: + return (cur > best && cur > req); + case CRA_PRIO_HIGHEST: + return (cur > best); + default: + BUG(); + } + return 0; +} + +static struct crypto_alg *crypto_alg_lookup_by_priority(const char *name, + int prio_requested, enum prio_flags prio_flags) { struct crypto_alg *q, *alg = NULL; int best = -1; @@ -54,7 +75,8 @@ static struct crypto_alg *crypto_alg_loo exact = !strcmp(q->cra_driver_name, name); fuzzy = !strcmp(q->cra_name, name); - if (!exact && !(fuzzy && q->cra_priority > best)) + if (!exact && !(fuzzy && prio_compare(prio_requested, best, + q->cra_priority, prio_flags))) continue; if (unlikely(!crypto_alg_get(q))) @@ -73,6 +95,11 @@ static struct crypto_alg *crypto_alg_loo return alg; } +static struct crypto_alg *crypto_alg_lookup(const char *name) +{ + return crypto_alg_lookup_by_priority(name, 0, CRA_PRIO_HIGHEST); +} + /* A far more intelligent version of this is planned. For now, just * try an exact match on the name of the algorithm. */ static inline struct crypto_alg *crypto_alg_mod_lookup(const char *name) @@ -168,16 +195,12 @@ static unsigned int crypto_ctxsize(struc return len + alg->cra_alignmask; } -struct crypto_tfm *crypto_alloc_tfm(const char *name, u32 flags) +static struct crypto_tfm *crypto_alloc_tfm_from_alg(struct crypto_alg *alg, + u32 flags) { struct crypto_tfm *tfm = NULL; - struct crypto_alg *alg; unsigned int tfm_size; - alg = crypto_alg_mod_lookup(name); - if (alg == NULL) - goto out; - tfm_size = sizeof(*tfm) + crypto_ctxsize(alg, flags); tfm = kmalloc(tfm_size, GFP_KERNEL); if (tfm == NULL) @@ -206,6 +229,36 @@ out: return tfm; } +struct crypto_tfm *crypto_alloc_tfm(const char *name, u32 flags) +{ + struct crypto_alg *alg; + struct crypto_tfm *tfm = NULL; + + alg = crypto_alg_mod_lookup(name); + if (alg == NULL) + goto out; + tfm = crypto_alloc_tfm_from_alg(alg, flags); + +out: + return tfm; +} + +struct crypto_tfm *crypto_alloc_tfm_by_priority(const char *name, + u32 flags, int priority, enum prio_flags prio_flags) +{ + struct crypto_alg *alg; + struct crypto_tfm *tfm = NULL; + + alg = crypto_alg_lookup_by_priority(name, priority, prio_flags); + if (alg == NULL) + goto out; + + tfm = crypto_alloc_tfm_from_alg(alg, flags); + +out: + return tfm; +} + void crypto_free_tfm(struct crypto_tfm *tfm) { struct crypto_alg *alg; @@ -321,5 +374,6 @@ __initcall(init_crypto); EXPORT_SYMBOL_GPL(crypto_register_alg); EXPORT_SYMBOL_GPL(crypto_unregister_alg); EXPORT_SYMBOL_GPL(crypto_alloc_tfm); +EXPORT_SYMBOL_GPL(crypto_alloc_tfm_by_priority); EXPORT_SYMBOL_GPL(crypto_free_tfm); EXPORT_SYMBOL_GPL(crypto_alg_available); Index: linux/include/linux/crypto.h =================================================================== --- linux.orig/include/linux/crypto.h +++ linux/include/linux/crypto.h @@ -247,6 +247,17 @@ struct crypto_tfm { struct crypto_tfm *crypto_alloc_tfm(const char *alg_name, u32 tfm_flags); void crypto_free_tfm(struct crypto_tfm *tfm); +enum prio_flags { + CRA_PRIO_LOWEST = 1, + CRA_PRIO_LOWER_THAN = 2, + CRA_PRIO_EXACT = 3, + CRA_PRIO_HIGHER_THAN = 4, + CRA_PRIO_HIGHEST = 5, +}; + +struct crypto_tfm *crypto_alloc_tfm_by_priority(const char *alg_name, + u32 tfm_flags, int priority, enum prio_flags prio_flags); + /* * Transform helpers which query the underlying algorithm. */
Index: linux/drivers/crypto/padlock-sha.c =================================================================== --- /dev/null +++ linux/drivers/crypto/padlock-sha.c @@ -0,0 +1,264 @@ +/* + * Cryptographic API. + * + * Support for VIA PadLock hardware crypto engine. + * + * Copyright (c) 2006 Michal Ludvig <[EMAIL PROTECTED]> + * + * 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/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/crypto.h> +#include <linux/cryptohash.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/scatterlist.h> +#include <asm/byteorder.h> +#include "padlock.h" + +#define PADLOCK_CRA_PRIORITY 300 + +#define SHA1_DEFAULT_FALLBACK "sha1-generic" +#define SHA1_DIGEST_SIZE 20 +#define SHA1_HMAC_BLOCK_SIZE 64 + +#define SHA256_DEFAULT_FALLBACK "sha256-generic" +#define SHA256_DIGEST_SIZE 32 +#define SHA256_HMAC_BLOCK_SIZE 64 + +static char *sha1_fallback = SHA1_DEFAULT_FALLBACK; +static char *sha256_fallback = SHA256_DEFAULT_FALLBACK; + +module_param(sha1_fallback, charp, 0444); +module_param(sha256_fallback, charp, 0444); + +MODULE_PARM_DESC(sha1_fallback, "Fallback driver for SHA1. Default is " SHA1_DEFAULT_FALLBACK); +MODULE_PARM_DESC(sha256_fallback, "Fallback driver for SHA256. Default is " SHA256_DEFAULT_FALLBACK); + +struct padlock_sha_ctx { + char *data; + size_t used; + int bypass; + void (*f_sha_padlock)(const char *in, char *out, int count); + const char *fallback_driver_name; + struct crypto_tfm *fallback_tfm; +}; + +#define CTX(tfm) ((struct padlock_sha_ctx*)(crypto_tfm_ctx(tfm))) + +static struct crypto_tfm *tfm_sha1, *tfm_sha256; +static struct crypto_alg sha1_alg, sha256_alg; + +static void padlock_sha_bypass(struct crypto_tfm *tfm) +{ + if (CTX(tfm)->bypass) + return; + + /* We're attempting to use ALG from a module of the same name, + * e.g. sha1 algo from sha1.ko. This could be more intelligent and + * allow e.g. sha1-i586 module to be used instead. Hmm, maybe later. + * + * BTW We assume we get a valid TFM. There is no error-path from + * digest.dia_init(). + */ + CTX(tfm)->fallback_tfm = crypto_alloc_tfm(CTX(tfm)->fallback_driver_name, 0); + BUG_ON(!CTX(tfm)->fallback_tfm); + + crypto_digest_init(CTX(tfm)->fallback_tfm); + if (CTX(tfm)->data && CTX(tfm)->used) { + struct scatterlist sg[8]; + + sg_set_buf(&sg[0], CTX(tfm)->data, CTX(tfm)->used); + crypto_digest_update(CTX(tfm)->fallback_tfm, sg, 1); + } + + free_page((unsigned long)(CTX(tfm)->data)); + CTX(tfm)->data = NULL; + 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; + CTX(tfm)->data = (char*)__get_free_page(GFP_KERNEL); + if (!CTX(tfm)->data) + padlock_sha_bypass(tfm); + return; +} + +static void padlock_sha_update(struct crypto_tfm *tfm, const uint8_t *data, unsigned int length) +{ + if (unlikely(!CTX(tfm)->bypass && (CTX(tfm)->used + length > PAGE_SIZE))) + padlock_sha_bypass(tfm); + + if (unlikely(CTX(tfm)->bypass)) { + struct scatterlist sg[8]; + BUG_ON(!CTX(tfm)->fallback_tfm); + sg_set_buf(&sg[0], data, length); + crypto_digest_update(CTX(tfm)->fallback_tfm, sg, 1); + goto out_unlock; + } + + memcpy(CTX(tfm)->data + CTX(tfm)->used, data, length); + CTX(tfm)->used += length; + +out_unlock: + return; +} + +void padlock_do_sha1(const char *in, char *out, int count) +{ + BUG(); +} + +void padlock_do_sha256(const char *in, char *out, int count) +{ + BUG(); +} + +static void padlock_sha_final(struct crypto_tfm *tfm, uint8_t *out) +{ + padlock_sha_bypass(tfm); + + if (unlikely(CTX(tfm)->bypass)) { + BUG_ON(!CTX(tfm)->fallback_tfm); + crypto_digest_final(CTX(tfm)->fallback_tfm, out); + crypto_free_tfm(CTX(tfm)->fallback_tfm); + CTX(tfm)->fallback_tfm = NULL; + CTX(tfm)->bypass = 0; + return; + } + + /* Pass the input buffer to PadLock microcode... */ + CTX(tfm)->f_sha_padlock(CTX(tfm)->data, out, CTX(tfm)->used); + + if (CTX(tfm)->data) { + free_page((unsigned long)(CTX(tfm)->data)); + CTX(tfm)->data = NULL; + } + + CTX(tfm)->used = 0; +} + +static void padlock_sha1_init(struct crypto_tfm *tfm) +{ + CTX(tfm)->f_sha_padlock = padlock_do_sha1; + CTX(tfm)->fallback_driver_name = crypto_tfm_alg_driver_name(tfm_sha1); + + padlock_sha_init(tfm); +} + +static void padlock_sha256_init(struct crypto_tfm *tfm) +{ + CTX(tfm)->f_sha_padlock = padlock_do_sha256; + CTX(tfm)->fallback_driver_name = crypto_tfm_alg_driver_name(tfm_sha256); + + padlock_sha_init(tfm); +} + +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, + .cra_blocksize = SHA1_HMAC_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct padlock_sha_ctx), + .cra_alignmask = PADLOCK_ALIGNMENT - 1, + .cra_module = THIS_MODULE, + .cra_list = LIST_HEAD_INIT(sha1_alg.cra_list), + .cra_u = { + .digest = { + .dia_digestsize = SHA1_DIGEST_SIZE, + .dia_init = padlock_sha1_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, + .cra_blocksize = SHA256_HMAC_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct padlock_sha_ctx), + .cra_alignmask = PADLOCK_ALIGNMENT - 1, + .cra_module = THIS_MODULE, + .cra_list = LIST_HEAD_INIT(sha256_alg.cra_list), + .cra_u = { + .digest = { + .dia_digestsize = SHA256_DIGEST_SIZE, + .dia_init = padlock_sha256_init, + .dia_update = padlock_sha_update, + .dia_final = padlock_sha_final, + } + } +}; + +int __init padlock_init_sha(void) +{ + int rc = -ENOENT; + + /* We'll hold one TFM for each fallback + * to ensure the modules are loaded and available. */ + tfm_sha1 = crypto_alloc_tfm(sha1_fallback, 0); + if (!tfm_sha1) { + printk(KERN_WARNING PFX "Couldn't load fallback module for '%s'. Tried '%s'.\n", + sha1_alg.cra_name, sha1_fallback); + goto out; + } + printk(KERN_NOTICE PFX "Fallback for '%s' is driver '%s' (prio=%d)\n", sha1_alg.cra_name, + crypto_tfm_alg_driver_name(tfm_sha1), crypto_tfm_alg_priority(tfm_sha1)); + + tfm_sha256 = crypto_alloc_tfm(sha256_fallback, 0); + if (!tfm_sha256) { + printk(KERN_WARNING PFX "Couldn't load fallback module for '%s'. Tried '%s'.\n", + sha256_alg.cra_name, sha256_fallback); + goto out_free1; + } + printk(KERN_NOTICE PFX "Fallback for '%s' is driver '%s' (prio=%d)\n", sha256_alg.cra_name, + crypto_tfm_alg_driver_name(tfm_sha256), crypto_tfm_alg_priority(tfm_sha256)); + + rc = crypto_register_alg(&sha1_alg); + if (rc) + goto out_free256; + + 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_free256: + crypto_free_tfm(tfm_sha256); +out_free1: + crypto_free_tfm(tfm_sha1); +out: + return -ENOENT; +} + +void __exit padlock_fini_sha(void) +{ + crypto_free_tfm(tfm_sha1); + crypto_free_tfm(tfm_sha256); + crypto_unregister_alg(&sha1_alg); + crypto_unregister_alg(&sha256_alg); +} + +MODULE_ALIAS("sha1-padlock"); +MODULE_ALIAS("sha256-padlock"); Index: linux/drivers/crypto/Kconfig =================================================================== --- linux.orig/drivers/crypto/Kconfig +++ linux/drivers/crypto/Kconfig @@ -20,4 +20,11 @@ config CRYPTO_DEV_PADLOCK_AES help Use VIA PadLock for AES algorithm. +config CRYPTO_DEV_PADLOCK_SHA + bool "Support for SHA1/SHA256 in VIA PadLock" + depends on CRYPTO_DEV_PADLOCK + default y + help + Use VIA PadLock for SHA1/SHA256 algorithms. + endmenu Index: linux/drivers/crypto/Makefile =================================================================== --- linux.orig/drivers/crypto/Makefile +++ linux/drivers/crypto/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_CRYPTO_DEV_PADLOCK) += padlock.o padlock-objs-$(CONFIG_CRYPTO_DEV_PADLOCK_AES) += padlock-aes.o +padlock-objs-$(CONFIG_CRYPTO_DEV_PADLOCK_SHA) += padlock-sha.o padlock-objs := padlock-generic.o $(padlock-objs-y) Index: linux/drivers/crypto/padlock-generic.c =================================================================== --- linux.orig/drivers/crypto/padlock-generic.c +++ linux/drivers/crypto/padlock-generic.c @@ -24,6 +24,7 @@ padlock_init(void) { int ret = -ENOSYS; +#if 0 if (!cpu_has_xcrypt) { printk(KERN_ERR PFX "VIA PadLock not detected.\n"); return -ENODEV; @@ -33,6 +34,7 @@ padlock_init(void) printk(KERN_ERR PFX "VIA PadLock detected, but not enabled. Hmm, strange...\n"); return -ENODEV; } +#endif #ifdef CONFIG_CRYPTO_DEV_PADLOCK_AES if ((ret = padlock_init_aes())) { @@ -41,6 +43,13 @@ padlock_init(void) } #endif +#ifdef CONFIG_CRYPTO_DEV_PADLOCK_SHA + if ((ret = padlock_init_sha())) { + printk(KERN_ERR PFX "VIA PadLock SHA1/SHA256 initialization failed.\n"); + return ret; + } +#endif + if (ret == -ENOSYS) printk(KERN_ERR PFX "Hmm, VIA PadLock was compiled without any algorithm.\n"); @@ -53,6 +62,10 @@ padlock_fini(void) #ifdef CONFIG_CRYPTO_DEV_PADLOCK_AES padlock_fini_aes(); #endif + +#ifdef CONFIG_CRYPTO_DEV_PADLOCK_SHA + padlock_fini_sha(); +#endif } module_init(padlock_init); Index: linux/drivers/crypto/padlock.h =================================================================== --- linux.orig/drivers/crypto/padlock.h +++ linux/drivers/crypto/padlock.h @@ -33,4 +33,9 @@ int padlock_init_aes(void); void padlock_fini_aes(void); #endif +#ifdef CONFIG_CRYPTO_DEV_PADLOCK_SHA +int padlock_init_sha(void); +void padlock_fini_sha(void); +#endif + #endif /* _CRYPTO_PADLOCK_H */