11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	pci_syscall.c
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * For architectures where we want to allow direct access
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to the PCI config stuff - it would probably be preferable
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * on PCs too, but there people just do it by hand with the
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * magic northbridge registers..
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pci.h>
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/syscalls.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
14e04b0ea2e0f9c1bb0d874db4493fc7f7a623116bBrian King#include "pci.h"
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16c4ea37c26a691ad0b7e86aa5884aab27830e95c9Heiko CarstensSYSCALL_DEFINE5(pciconfig_read, unsigned long, bus, unsigned long, dfn,
17c4ea37c26a691ad0b7e86aa5884aab27830e95c9Heiko Carstens		unsigned long, off, unsigned long, len, void __user *, buf)
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct pci_dev *dev;
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 byte;
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 word;
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 dword;
23e4585da22ad04a055cbb5c863a37aa8cc02eac89Alan Cox	long err;
24e4585da22ad04a055cbb5c863a37aa8cc02eac89Alan Cox	long cfg_ret;
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!capable(CAP_SYS_ADMIN))
27e4585da22ad04a055cbb5c863a37aa8cc02eac89Alan Cox		return -EPERM;
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = -ENODEV;
30e4585da22ad04a055cbb5c863a37aa8cc02eac89Alan Cox	dev = pci_get_bus_and_slot(bus, dfn);
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dev)
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto error;
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (len) {
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 1:
36e04b0ea2e0f9c1bb0d874db4493fc7f7a623116bBrian King		cfg_ret = pci_user_read_config_byte(dev, off, &byte);
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 2:
39e04b0ea2e0f9c1bb0d874db4493fc7f7a623116bBrian King		cfg_ret = pci_user_read_config_word(dev, off, &word);
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 4:
42e04b0ea2e0f9c1bb0d874db4493fc7f7a623116bBrian King		cfg_ret = pci_user_read_config_dword(dev, off, &dword);
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = -EINVAL;
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto error;
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	};
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = -EIO;
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (cfg_ret != PCIBIOS_SUCCESSFUL)
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto error;
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (len) {
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 1:
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = put_user(byte, (unsigned char __user *)buf);
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 2:
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = put_user(word, (unsigned short __user *)buf);
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 4:
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = put_user(dword, (unsigned int __user *)buf);
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
63e4585da22ad04a055cbb5c863a37aa8cc02eac89Alan Cox	}
64e4585da22ad04a055cbb5c863a37aa8cc02eac89Alan Cox	pci_dev_put(dev);
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return err;
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserror:
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* ??? XFree86 doesn't even check the return value.  They
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   just look for 0xffffffff in the output, since that's what
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   they get instead of a machine check on x86.  */
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (len) {
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 1:
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_user(-1, (unsigned char __user *)buf);
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 2:
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_user(-1, (unsigned short __user *)buf);
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 4:
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_user(-1, (unsigned int __user *)buf);
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
81e4585da22ad04a055cbb5c863a37aa8cc02eac89Alan Cox	}
82e4585da22ad04a055cbb5c863a37aa8cc02eac89Alan Cox	pci_dev_put(dev);
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return err;
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
86c4ea37c26a691ad0b7e86aa5884aab27830e95c9Heiko CarstensSYSCALL_DEFINE5(pciconfig_write, unsigned long, bus, unsigned long, dfn,
87c4ea37c26a691ad0b7e86aa5884aab27830e95c9Heiko Carstens		unsigned long, off, unsigned long, len, void __user *, buf)
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct pci_dev *dev;
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 byte;
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 word;
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 dword;
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err = 0;
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!capable(CAP_SYS_ADMIN))
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EPERM;
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
98e4585da22ad04a055cbb5c863a37aa8cc02eac89Alan Cox	dev = pci_get_bus_and_slot(bus, dfn);
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dev)
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch(len) {
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 1:
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = get_user(byte, (u8 __user *)buf);
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (err)
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
107e04b0ea2e0f9c1bb0d874db4493fc7f7a623116bBrian King		err = pci_user_write_config_byte(dev, off, byte);
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (err != PCIBIOS_SUCCESSFUL)
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			err = -EIO;
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 2:
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = get_user(word, (u16 __user *)buf);
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (err)
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
116e04b0ea2e0f9c1bb0d874db4493fc7f7a623116bBrian King		err = pci_user_write_config_word(dev, off, word);
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (err != PCIBIOS_SUCCESSFUL)
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			err = -EIO;
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 4:
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = get_user(dword, (u32 __user *)buf);
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (err)
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
125e04b0ea2e0f9c1bb0d874db4493fc7f7a623116bBrian King		err = pci_user_write_config_dword(dev, off, dword);
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (err != PCIBIOS_SUCCESSFUL)
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			err = -EIO;
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = -EINVAL;
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
133e4585da22ad04a055cbb5c863a37aa8cc02eac89Alan Cox	}
134e4585da22ad04a055cbb5c863a37aa8cc02eac89Alan Cox	pci_dev_put(dev);
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return err;
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
137