/* * Cryptographic API. * * Support for VIA PadLock hardware crypto engine. * * Linux developers: * Michal Ludvig * * 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 #include #include #include #include #include #include "padlock.h" #include "../acrypto.h" #include "../crypto_def.h" #define PFX "padlock: " typedef void (xcrypt_t)(u8 *input, u8 *output, u8 *key, u8 *iv, void *control_word, u32 count); static inline void padlock_xcrypt_ecb(u8 *input, u8 *output, u8 *key, u8 *iv, void *control_word, u32 count) { asm volatile ("pushfl; popfl"); /* enforce key reload. */ asm volatile (".byte 0xf3,0x0f,0xa7,0xc8" /* rep xcryptecb */ : "=m"(*output), "+S"(input), "+D"(output) : "d"(control_word), "b"(key), "c"(count)); } static inline void padlock_xcrypt_cbc(u8 *input, u8 *output, u8 *key, u8 *iv, void *control_word, u32 count) { asm volatile ("pushfl; popfl"); /* enforce key reload. */ asm volatile (".byte 0xf3,0x0f,0xa7,0xd0" /* rep xcryptcbc */ : "=m"(*output), "+S"(input), "+D"(output) : "d"(control_word), "b"(key), "c"(count), "a"(iv)); } static inline void padlock_xcrypt_cfb(u8 *input, u8 *output, u8 *key, u8 *iv, void *control_word, u32 count) { asm volatile ("pushfl; popfl"); /* enforce key reload. */ asm volatile (".byte 0xf3,0x0f,0xa7,0xe0" /* rep xcryptcfb */ : "=m"(*output), "+S"(input), "+D"(output) : "d"(control_word), "b"(key), "c"(count), "a"(iv)); } static inline void padlock_xcrypt_ofb(u8 *input, u8 *output, u8 *key, u8 *iv, void *control_word, u32 count) { asm volatile ("pushfl; popfl"); /* enforce key reload. */ asm volatile (".byte 0xf3,0x0f,0xa7,0xe8" /* rep xcryptofb */ : "=m"(*output), "+S"(input), "+D"(output) : "d"(control_word), "b"(key), "c"(count), "a"(iv)); } void *crypto_aligned_kmalloc(size_t size, int mode, size_t alignment, void **index) { char *ptr; ptr = kmalloc(size + alignment, mode); *index = ptr; if (alignment > 1 && ((long)ptr & (alignment - 1))) { ptr += alignment - ((long)ptr & (alignment - 1)); } return ptr; } void padlock_aligner(u8 *out_arg, const u8 *in_arg, const u8 *iv_arg, void *key, union cword *cword, size_t nbytes, size_t blocksize, int encdec, int mode) { /* Don't blindly modify this structure - the items must fit on 16-Bytes boundaries! */ struct padlock_xcrypt_data { u8 iv[blocksize]; /* Initialization vector */ }; u8 *in, *out, *iv; void *index = NULL; char bigbuf[sizeof(struct padlock_xcrypt_data) + 16]; struct padlock_xcrypt_data *data; /* Place 'data' at the first 16-Bytes aligned address in 'bigbuf'. */ if (((long)bigbuf) & 0x0F) data = (void*)(bigbuf + 16 - ((long)bigbuf & 0x0F)); else data = (void*)bigbuf; if (((long)in_arg) & 0x0F) { in = crypto_aligned_kmalloc(nbytes, GFP_KERNEL, 16, &index); memcpy(in, in_arg, nbytes); } else in = (u8*)in_arg; if (((long)out_arg) & 0x0F) { if (index) out = in; /* xcrypt can work "in place" */ else out = crypto_aligned_kmalloc(nbytes, GFP_KERNEL, 16, &index); } else out = out_arg; /* Always make a local copy of IV - xcrypt may change it! */ iv = data->iv; if (iv_arg) memcpy(iv, iv_arg, blocksize); dprintk("data=%p\n", data); dprintk("in=%p\n", in); dprintk("out=%p\n", out); dprintk("iv=%p\n", iv); dprintk("nbytes=%d, blocksize=%d.\n", nbytes, blocksize); switch (mode) { case CRYPTO_MODE_ECB: padlock_xcrypt_ecb(in, out, key, iv, cword, nbytes/blocksize); break; case CRYPTO_MODE_CBC: padlock_xcrypt_cbc(in, out, key, iv, cword, nbytes/blocksize); break; case CRYPTO_MODE_CFB: padlock_xcrypt_cfb(in, out, key, iv, cword, nbytes/blocksize); break; case CRYPTO_MODE_OFB: padlock_xcrypt_ofb(in, out, key, iv, cword, nbytes/blocksize); break; default: BUG(); } /* Copy the 16-Byte aligned output to the caller's buffer. */ if (out != out_arg) memcpy(out_arg, out, nbytes); if (index) kfree(index); } static int __init padlock_init(void) { int ret = -ENOSYS; #if 0 if (!cpu_has_xcrypt) { printk(KERN_ERR PFX "VIA PadLock not detected.\n"); return -ENODEV; } if (!cpu_has_xcrypt_enabled) { printk(KERN_ERR PFX "VIA PadLock detected, but not enabled. Hmm, strange...\n"); return -ENODEV; } #endif if ((ret = padlock_init_aes())) { printk(KERN_ERR PFX "VIA PadLock AES initialization failed.\n"); return ret; } if (ret == -ENOSYS) printk(KERN_ERR PFX "Hmm, VIA PadLock was compiled without any algorithm.\n"); return ret; } static void __exit padlock_fini(void) { padlock_fini_aes(); } module_init(padlock_init); module_exit(padlock_fini); MODULE_DESCRIPTION("VIA PadLock crypto engine support."); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Michal Ludvig");