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