📄 geode_crypto.c
字号:
/* * Driver for the Geode LX hardware AES encryption module * Copyright (C) 2003 Advanced Micro Devices, Inc. All Rights Reserved. */#include <linux/module.h>#include <linux/version.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/pci.h>#include <linux/pci_ids.h>#include <linux/miscdevice.h>#include <asm/io.h>#include <asm/msr.h>#include <asm/uaccess.h>#include <asm/delay.h>#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)#include <asm/cacheflush.h>#endif#ifdef CONFIG_CRYPTO#include <linux/crypto.h>#endif#include "geode_crypto.h"#include "build_num.h"#define ERROR(str,args...) printk(KERN_ERR "GEODECRYPTO: " str, ##args)#define WARNING(str,args...) printk(KERN_INFO "GEODECRYPTO: " str, ##args)/* The PCI id of the device */#define PCI_DEVICE_ID_GEODE_AES 0x2082/* The master driver (kmalloed to save bss space) */struct geode_crypt_driver *crypto_driver = 0;struct geode_crypt_ksession *get_session(struct geode_crypt_driver *driver, int id) { struct crypt_slist *slist = driver->session_list; for( ; slist; slist = slist->next) if (slist->id == id) return &slist->session; return 0;}void aes_write_field(struct geode_crypt_driver *driver, u32 offset, void *value) { u32 *v = (u32 *) value; /* Don't forget, its little endian */ writel(v[0], driver->base + offset); writel(v[1], driver->base + offset + 0x04); writel(v[2], driver->base + offset + 0x08); writel(v[3], driver->base + offset + 0x0C);} #define SET_KEY(driver, key) aes_write_field(driver, AES_WRITEKEY0_REG, key)#define SET_IV(driver, iv) aes_write_field(driver, AES_WRITEIV0_REG, iv)voidaes_sync_block(struct geode_crypt_driver *driver, struct geode_crypt_ksession *session, int flags) { u32 ctrl = AES_CTRL_START, intr = 0; u32 status; intr = readl(driver->base + AES_INTERRUPT_REG); writel(intr | (AES_INTR_A_MASK | AES_INTR_B_MASK), driver->base + AES_INTERRUPT_REG); writel(__pa(session->src), driver->base + AES_SOURCEA_REG); writel(__pa(session->dest), driver->base + AES_DSTA_REG); writel(session->blocksize, driver->base + AES_LENA_REG); if (session->flags & CRYPTO_SESSION_CBC) ctrl |= AES_CTRL_CBCA; if (!(session->flags & CRYPTO_SESSION_HWKEY)) ctrl |= AES_CTRL_WRKEY; /* Coherency is the default, the flags need to disable it */ if (!(session->flags & CRYPTO_SESSION_NOCOHERENCY)) ctrl |= AES_CTRL_SCA | AES_CTRL_DCA; if (flags & CRYPTO_FLAG_ENCRYPT) ctrl |= AES_CTRL_ENCRYPT; writel(ctrl, driver->base + AES_CONTROLA_REG); status = readl(driver->base + AES_INTERRUPT_REG); while(!(status & AES_INTR_A_PENDING)) { udelay(10); status = readl(driver->base + AES_INTERRUPT_REG); } writel(AES_INTR_A_PENDING | intr, driver->base + AES_INTERRUPT_REG);}int aes_crypt_sync(struct geode_crypt_driver *driver, struct geode_crypt_op *crypt) { struct geode_crypt_ksession *session = get_session(driver, crypt->id); u32 rem = crypt->len, pos = 0; if (!session) return -1; if (down_interruptible(&driver->engine_mutex)) return -EINTR; if (!(session->flags & CRYPTO_SESSION_HWKEY)) SET_KEY(driver, (u8 *) session->key); if ((session->flags & CRYPTO_SESSION_CBC) && crypt->iv) { u32 iv[4]; copy_from_user((u8 *) iv, crypt->iv, CRYPTO_KEY_LEN); SET_IV(driver, (u8 *) iv); } while(rem) { int bsize = session->blocksize; int copy = (rem < bsize) ? rem : bsize; if (copy_from_user(session->src, crypt->src + pos, copy)) return -EFAULT; if (copy < bsize) memset(session->src + copy, 0, bsize - copy); /* Clear the destination just to make sure */ memset(session->dest, 0, session->blocksize); aes_sync_block(driver, session, crypt->flags); if (copy_to_user(crypt->dst + pos, session->dest, copy)) return -EFAULT; rem -= copy; pos += copy; } up(&driver->engine_mutex); return 0;}void geode_free_session(struct geode_crypt_driver *driver, int id) { struct crypt_slist *prev = 0, *slist = driver->session_list; for( ; slist; prev = slist, slist = slist->next) { struct geode_crypt_ksession *s = &slist->session; if (slist->id != id) continue; if (prev) prev->next = slist->next; else driver->session_list = slist->next; if (s->src) free_pages((u32) s->src, get_order(s->blocksize)); if (s->dest) free_pages((u32) s->dest, get_order(s->blocksize)); kfree(slist); break; }} int geode_create_session(struct geode_crypt_driver *driver, struct geode_crypt_ksession *session) { struct crypt_slist *cur = 0; struct geode_crypt_ksession *s = 0; int order = 0; if (session->blocksize % 16) { printk(KERN_ERR "GEODECRYPTO: Block size %d is invalid.\n", session->blocksize); return -EINVAL; } order = get_order(session->blocksize); if (order > 9) { printk(KERN_ERR "GEODECRYPTO: Block size of %d is too big\n", session->blocksize); return -EINVAL; } if (!(cur = kmalloc(sizeof(*cur), GFP_KERNEL))) return -ENOMEM; s = &cur->session; cur->id = driver->session_index++; cur->next = 0; memcpy(s, session, sizeof(*session)); s->src = (void *) __get_free_pages(GFP_KERNEL, order); s->dest = (void *) __get_free_pages(GFP_KERNEL, order); if (!s->src || !s->dest) { if (s->src) free_pages((u32) s->src, order); if (s->dest) free_pages((u32) s->dest, order); kfree(cur); printk(KERN_ERR "GEODECRYPTO: Unable to allocate memory pages\n"); return -ENOMEM; } if (!(session->flags & CRYPTO_SESSION_NOCOHERENCY)) { change_page_attr(virt_to_page(s->src), order, PAGE_KERNEL_NOCACHE); change_page_attr(virt_to_page(s->dest), order, PAGE_KERNEL_NOCACHE); } if (!driver->session_list) driver->session_list = cur; else { struct crypt_slist *l = driver->session_list; for( ; l->next; l = l->next); l->next = cur; } return cur->id;} static int geode_crypt_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { struct geode_crypt_driver *driver = (struct geode_crypt_driver *) filp->private_data; switch(cmd) { case CIOCGSESSION: { struct geode_crypt_ksession session; memset(&session, 0, sizeof(session)); if (copy_from_user(&session, (void *) arg, sizeof(struct geode_crypt_session))) return -EFAULT; session.id = geode_create_session(driver, &session); if (session.id < 0) return session.id; if (copy_to_user((void *) arg, &session, sizeof(struct geode_crypt_session))) return -EFAULT; return 0; } case CIOCFSESSION: geode_free_session(driver, (int) arg); return 0; case CIOCSKEY: { struct geode_crypt_key key; struct geode_crypt_ksession *session; if (copy_from_user(&key, (void *) arg, sizeof(key))) return -EFAULT; session = get_session(driver, key.id); if (!session) return -EINVAL; memcpy(session->key, key.key, CRYPTO_KEY_LEN); return 0; } case CIOCCRYPT: { struct geode_crypt_op crypt; if (copy_from_user(&crypt, (void *) arg, sizeof(crypt))) return -EFAULT; /* Sanity check */ if (!crypt.src || !crypt.dst) return -EFAULT; return aes_crypt_sync(driver, &crypt); } } return -EINVAL;}static int geode_crypt_open(struct inode * inode, struct file * filp) { struct geode_crypt_driver *driver = (struct geode_crypt_driver *) crypto_driver; filp->private_data = driver; if (driver->session_list) BUG(); if (down_trylock(&driver->dev_mutex)) return -EBUSY; /* Only one at a time please */ return 0;}static int geode_crypt_release(struct inode * inode, struct file * filp) { struct geode_crypt_driver *driver = (struct geode_crypt_driver *) filp->private_data; /* FIXME: Should sessions survive through a close? */ if (driver->session_list) { struct crypt_slist *slist = driver->session_list; while(slist) { struct crypt_slist *cur = slist; struct geode_crypt_ksession *s = &cur->session; slist = cur->next; if (s->src) free_pages((u32) s->src, get_order(s->blocksize)); if (s->dest) free_pages((u32) s->dest, get_order(s->blocksize)); kfree(cur); } driver->session_list = 0; } up(&driver->dev_mutex); return 0;}static struct file_operations geode_crypt_fops = { owner: THIS_MODULE, ioctl: geode_crypt_ioctl, open: geode_crypt_open, release: geode_crypt_release,}; static struct miscdevice geode_crypt_device = { GEODE_CRYPT_MINOR_DEV, "geodecrypto", &geode_crypt_fops};/* Defined in eeprom.c */extern struct miscdevice geode_eeprom_device;#ifdef CONFIG_CRYPTO#define GEODE_AES_BLOCK_SIZE 16 #define GEODE_AES_KEY_SIZE CRYPTO_KEY_LEN#define GEODE_AES_IV_SIZE CRYPTO_KEY_LENstatic struct geode_crypt_ksession cryptoapi_session = { id: 0, blocksize: GEODE_AES_BLOCK_SIZE, flags: CRYPTO_SESSION_ECB};static int geode_aes_setkey(void *data, const u8 *key, unsigned int len, u32 *flags) { if (len != GEODE_AES_KEY_SIZE) { *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; return -EINVAL; } memcpy(&cryptoapi_session.key, key, GEODE_AES_KEY_SIZE); return 0;} /* aes_crypto_encrypt() encrypt the specified data from the crypto API */static void geode_aes_encrypt(void *data, u8 *out, const u8 *in) { if (!out || !in) return; mode = AES_FLAG_ENCRYPT; cryptoapi_session.in = in; cryptoapi_session.out = out; aes_sync_start(crypto_driver, &cryptoapi_session, mode);}static void geode_aes_decrypt(void *data, u8 *out, const u8 *in) {2 if (!out || !in) return; mode = AES_FLAG_DECRYPT; cryptoapi_session.in = in; cryptoapi_session.out = out; aes_sync_start(crypto_driver, &cryptoapi_session, mode);}static struct crypto_alg geode_aes_crypto = { .cra_name = "geodeaes", .cra_flags = CRYPTO_ALG_TYPE_CIPHER, .cra_blocksize = GEODE_AES_BLOCK_SIZE, .cra_ctxsize = 0, .cra_module = THIS_MODULE, .cra_list = LIST_HEAD_INIT(daes_crypto.cra_list), .cra_u = { .cipher = { .cia_min_keysize = GEODE_AES_KEY_SIZE, .cia_max_keysize = GEODE_AES_KEY_SIZE, .cia_ivsize = GEODE_AES_IV_SIZE, .cia_setkey = geode_aes_setkey, .cia_encrypt = geode_aes_encrypt, .cia_decrypt = geode_aes_decrypt } }};#endifstatic int geode_aes_probe(struct pci_dev *dev, const struct pci_device_id *id) { int ret = 0; unsigned long ioaddr, iolen; ret = pci_enable_device(dev); if (ret) return ret; /* Get our resources - should be just the mmeory mapped region */ ioaddr = pci_resource_start(dev, 0); iolen = pci_resource_len(dev, 0); pci_request_regions(dev, "Geode AES module"); crypto_driver = (struct geode_crypt_driver *) kmalloc(sizeof(*crypto_driver), GFP_KERNEL); if (!crypto_driver) return -ENOMEM; memset(crypto_driver, 0, sizeof(*crypto_driver));; /* Set up the memory block */ if (!(crypto_driver->base = (u8 *) ioremap(ioaddr, iolen))) { ERROR("Unable to map the registers [%lx]\n", ioaddr); kfree(crypto_driver); crypto_driver = 0; return -ENOMEM; } /* Clear any pending AES calls */ writel(AES_INTR_PENDING | ~(AES_INTR_A_MASK | AES_INTR_B_MASK), crypto_driver->base + AES_INTERRUPT_REG); /* semaphore for accessing the engine */ init_MUTEX(&crypto_driver->engine_mutex); /* semaphore for accessing the device file */ init_MUTEX(&crypto_driver->dev_mutex); init_MUTEX(&crypto_driver->eeprom_mutex); /* Register the AES interface */ if ((ret = misc_register(&geode_crypt_device))) { ERROR("Unable to register the crypto device\n"); kfree(crypto_driver); crypto_driver = 0; return ret; } if (misc_register(&geode_eeprom_device)) WARNING("Unable to register the eeprom device\n"); else crypto_driver->flags |= 1; #ifdef CONFIG_CRYPTO if (crypto_register_alg(&geode_aes_crypto)) WARNING("Unable to register the cryptoAPI module.\n"); else crypto_driver->flags |= 2;#endif return 0;}/* aes_remove() Remove the device and go away*/static void __devexit geode_aes_remove(struct pci_dev *pdev) { /* If crypto_driver is null, then nothing was really set up */ if (crypto_driver) {#ifdef CONFIG_CRYPTO if (crypto_driver->flags & 2) crypto_unregister_alg(&geode_aes_crypto);#endif misc_deregister(&geode_crypt_device); if (crypto_driver->flags & 1) misc_deregister(&geode_eeprom_device); if (crypto_driver->session_list) BUG(); if (crypto_driver->base) iounmap(crypto_driver->base); kfree(crypto_driver); crypto_driver = 0; } pci_release_regions (pdev); pci_disable_device (pdev);} struct pci_device_id geode_aes_tbl[] = { { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_GEODE_AES, PCI_ANY_ID, PCI_ANY_ID} , { 0, }};MODULE_DEVICE_TABLE(pci, geode_aes_tbl);struct pci_driver aes_pci_driver = { name: "Geode GX AES drver", id_table: geode_aes_tbl, probe: geode_aes_probe, remove: __devexit_p(geode_aes_remove)};static int __init geode_aes_init(void){ printk(KERN_INFO "%s\n", AMD_VERSION); return pci_module_init(&aes_pci_driver); }static void __exit geode_aes_exit(void){ pci_unregister_driver(&aes_pci_driver);}MODULE_AUTHOR("Advanced Micro Devices, Inc.");MODULE_DESCRIPTION("Geode LX Hardware AES driver");MODULE_LICENSE("GPL");module_init(geode_aes_init);module_exit(geode_aes_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -