⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 geode_crypto.c

📁 linux for amd development lx8
💻 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 + -