| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *	pci_syscall.c | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * For architectures where we want to allow direct access | 
					
						
							|  |  |  |  * to the PCI config stuff - it would probably be preferable | 
					
						
							|  |  |  |  * on PCs too, but there people just do it by hand with the | 
					
						
							|  |  |  |  * magic northbridge registers.. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/errno.h>
 | 
					
						
							|  |  |  | #include <linux/pci.h>
 | 
					
						
							|  |  |  | #include <linux/syscalls.h>
 | 
					
						
							|  |  |  | #include <asm/uaccess.h>
 | 
					
						
							| 
									
										
										
										
											2005-09-27 01:21:55 -07:00
										 |  |  | #include "pci.h"
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-14 14:14:28 +01:00
										 |  |  | SYSCALL_DEFINE5(pciconfig_read, unsigned long, bus, unsigned long, dfn, | 
					
						
							|  |  |  | 		unsigned long, off, unsigned long, len, void __user *, buf) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct pci_dev *dev; | 
					
						
							|  |  |  | 	u8 byte; | 
					
						
							|  |  |  | 	u16 word; | 
					
						
							|  |  |  | 	u32 dword; | 
					
						
							| 
									
										
										
										
											2007-04-23 14:57:37 +01:00
										 |  |  | 	long err; | 
					
						
							|  |  |  | 	long cfg_ret; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!capable(CAP_SYS_ADMIN)) | 
					
						
							| 
									
										
										
										
											2007-04-23 14:57:37 +01:00
										 |  |  | 		return -EPERM; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	err = -ENODEV; | 
					
						
							| 
									
										
										
										
											2007-04-23 14:57:37 +01:00
										 |  |  | 	dev = pci_get_bus_and_slot(bus, dfn); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (!dev) | 
					
						
							|  |  |  | 		goto error; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (len) { | 
					
						
							|  |  |  | 	case 1: | 
					
						
							| 
									
										
										
										
											2005-09-27 01:21:55 -07:00
										 |  |  | 		cfg_ret = pci_user_read_config_byte(dev, off, &byte); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case 2: | 
					
						
							| 
									
										
										
										
											2005-09-27 01:21:55 -07:00
										 |  |  | 		cfg_ret = pci_user_read_config_word(dev, off, &word); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case 4: | 
					
						
							| 
									
										
										
										
											2005-09-27 01:21:55 -07:00
										 |  |  | 		cfg_ret = pci_user_read_config_dword(dev, off, &dword); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		err = -EINVAL; | 
					
						
							|  |  |  | 		goto error; | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = -EIO; | 
					
						
							|  |  |  | 	if (cfg_ret != PCIBIOS_SUCCESSFUL) | 
					
						
							|  |  |  | 		goto error; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (len) { | 
					
						
							|  |  |  | 	case 1: | 
					
						
							|  |  |  | 		err = put_user(byte, (unsigned char __user *)buf); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case 2: | 
					
						
							|  |  |  | 		err = put_user(word, (unsigned short __user *)buf); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case 4: | 
					
						
							|  |  |  | 		err = put_user(dword, (unsigned int __user *)buf); | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2007-04-23 14:57:37 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	pci_dev_put(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  | 	/* ??? XFree86 doesn't even check the return value.  They
 | 
					
						
							|  |  |  | 	   just look for 0xffffffff in the output, since that's what | 
					
						
							|  |  |  | 	   they get instead of a machine check on x86.  */ | 
					
						
							|  |  |  | 	switch (len) { | 
					
						
							|  |  |  | 	case 1: | 
					
						
							|  |  |  | 		put_user(-1, (unsigned char __user *)buf); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case 2: | 
					
						
							|  |  |  | 		put_user(-1, (unsigned short __user *)buf); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case 4: | 
					
						
							|  |  |  | 		put_user(-1, (unsigned int __user *)buf); | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2007-04-23 14:57:37 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	pci_dev_put(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-14 14:14:28 +01:00
										 |  |  | SYSCALL_DEFINE5(pciconfig_write, unsigned long, bus, unsigned long, dfn, | 
					
						
							|  |  |  | 		unsigned long, off, unsigned long, len, void __user *, buf) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct pci_dev *dev; | 
					
						
							|  |  |  | 	u8 byte; | 
					
						
							|  |  |  | 	u16 word; | 
					
						
							|  |  |  | 	u32 dword; | 
					
						
							|  |  |  | 	int err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!capable(CAP_SYS_ADMIN)) | 
					
						
							|  |  |  | 		return -EPERM; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-23 14:57:37 +01:00
										 |  |  | 	dev = pci_get_bus_and_slot(bus, dfn); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (!dev) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch(len) { | 
					
						
							|  |  |  | 	case 1: | 
					
						
							|  |  |  | 		err = get_user(byte, (u8 __user *)buf); | 
					
						
							|  |  |  | 		if (err) | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2005-09-27 01:21:55 -07:00
										 |  |  | 		err = pci_user_write_config_byte(dev, off, byte); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (err != PCIBIOS_SUCCESSFUL) | 
					
						
							|  |  |  | 			err = -EIO; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case 2: | 
					
						
							|  |  |  | 		err = get_user(word, (u16 __user *)buf); | 
					
						
							|  |  |  | 		if (err) | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2005-09-27 01:21:55 -07:00
										 |  |  | 		err = pci_user_write_config_word(dev, off, word); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (err != PCIBIOS_SUCCESSFUL) | 
					
						
							|  |  |  | 			err = -EIO; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case 4: | 
					
						
							|  |  |  | 		err = get_user(dword, (u32 __user *)buf); | 
					
						
							|  |  |  | 		if (err) | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2005-09-27 01:21:55 -07:00
										 |  |  | 		err = pci_user_write_config_dword(dev, off, dword); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (err != PCIBIOS_SUCCESSFUL) | 
					
						
							|  |  |  | 			err = -EIO; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		err = -EINVAL; | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2007-04-23 14:57:37 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	pci_dev_put(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return err; | 
					
						
							|  |  |  | } |