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