138 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * For architectures where we want to allow direct access to the PCI config4 * stuff - it would probably be preferable on PCs too, but there people5 * just do it by hand with the magic northbridge registers.6 */7 8#include <linux/errno.h>9#include <linux/pci.h>10#include <linux/security.h>11#include <linux/syscalls.h>12#include <linux/uaccess.h>13#include "pci.h"14 15SYSCALL_DEFINE5(pciconfig_read, unsigned long, bus, unsigned long, dfn,16 unsigned long, off, unsigned long, len, void __user *, buf)17{18 struct pci_dev *dev;19 u8 byte;20 u16 word;21 u32 dword;22 int err, cfg_ret;23 24 err = -EPERM;25 dev = NULL;26 if (!capable(CAP_SYS_ADMIN))27 goto error;28 29 err = -ENODEV;30 dev = pci_get_domain_bus_and_slot(0, bus, dfn);31 if (!dev)32 goto error;33 34 switch (len) {35 case 1:36 cfg_ret = pci_user_read_config_byte(dev, off, &byte);37 break;38 case 2:39 cfg_ret = pci_user_read_config_word(dev, off, &word);40 break;41 case 4:42 cfg_ret = pci_user_read_config_dword(dev, off, &dword);43 break;44 default:45 err = -EINVAL;46 goto error;47 }48 49 err = -EIO;50 if (cfg_ret)51 goto error;52 53 switch (len) {54 case 1:55 err = put_user(byte, (u8 __user *)buf);56 break;57 case 2:58 err = put_user(word, (u16 __user *)buf);59 break;60 case 4:61 err = put_user(dword, (u32 __user *)buf);62 break;63 }64 pci_dev_put(dev);65 return err;66 67error:68 /* ??? XFree86 doesn't even check the return value. They69 just look for 0xffffffff in the output, since that's what70 they get instead of a machine check on x86. */71 switch (len) {72 case 1:73 put_user(-1, (u8 __user *)buf);74 break;75 case 2:76 put_user(-1, (u16 __user *)buf);77 break;78 case 4:79 put_user(-1, (u32 __user *)buf);80 break;81 }82 pci_dev_put(dev);83 return err;84}85 86SYSCALL_DEFINE5(pciconfig_write, unsigned long, bus, unsigned long, dfn,87 unsigned long, off, unsigned long, len, void __user *, buf)88{89 struct pci_dev *dev;90 u8 byte;91 u16 word;92 u32 dword;93 int err = 0;94 95 if (!capable(CAP_SYS_ADMIN) ||96 security_locked_down(LOCKDOWN_PCI_ACCESS))97 return -EPERM;98 99 dev = pci_get_domain_bus_and_slot(0, bus, dfn);100 if (!dev)101 return -ENODEV;102 103 switch (len) {104 case 1:105 err = get_user(byte, (u8 __user *)buf);106 if (err)107 break;108 err = pci_user_write_config_byte(dev, off, byte);109 if (err)110 err = -EIO;111 break;112 113 case 2:114 err = get_user(word, (u16 __user *)buf);115 if (err)116 break;117 err = pci_user_write_config_word(dev, off, word);118 if (err)119 err = -EIO;120 break;121 122 case 4:123 err = get_user(dword, (u32 __user *)buf);124 if (err)125 break;126 err = pci_user_write_config_dword(dev, off, dword);127 if (err)128 err = -EIO;129 break;130 131 default:132 err = -EINVAL;133 break;134 }135 pci_dev_put(dev);136 return err;137}138