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/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        long err;
  23        int cfg_ret;
  24
  25        if (!capable(CAP_SYS_ADMIN))
  26                return -EPERM;
  27
  28        err = -ENODEV;
  29        dev = pci_get_domain_bus_and_slot(0, bus, dfn);
  30        if (!dev)
  31                goto error;
  32
  33        switch (len) {
  34        case 1:
  35                cfg_ret = pci_user_read_config_byte(dev, off, &byte);
  36                break;
  37        case 2:
  38                cfg_ret = pci_user_read_config_word(dev, off, &word);
  39                break;
  40        case 4:
  41                cfg_ret = pci_user_read_config_dword(dev, off, &dword);
  42                break;
  43        default:
  44                err = -EINVAL;
  45                goto error;
  46        }
  47
  48        err = -EIO;
  49        if (cfg_ret)
  50                goto error;
  51
  52        switch (len) {
  53        case 1:
  54                err = put_user(byte, (unsigned char __user *)buf);
  55                break;
  56        case 2:
  57                err = put_user(word, (unsigned short __user *)buf);
  58                break;
  59        case 4:
  60                err = put_user(dword, (unsigned int __user *)buf);
  61                break;
  62        }
  63        pci_dev_put(dev);
  64        return err;
  65
  66error:
  67        /* ??? XFree86 doesn't even check the return value.  They
  68           just look for 0xffffffff in the output, since that's what
  69           they get instead of a machine check on x86.  */
  70        switch (len) {
  71        case 1:
  72                put_user(-1, (unsigned char __user *)buf);
  73                break;
  74        case 2:
  75                put_user(-1, (unsigned short __user *)buf);
  76                break;
  77        case 4:
  78                put_user(-1, (unsigned int __user *)buf);
  79                break;
  80        }
  81        pci_dev_put(dev);
  82        return err;
  83}
  84
  85SYSCALL_DEFINE5(pciconfig_write, unsigned long, bus, unsigned long, dfn,
  86                unsigned long, off, unsigned long, len, void __user *, buf)
  87{
  88        struct pci_dev *dev;
  89        u8 byte;
  90        u16 word;
  91        u32 dword;
  92        int err = 0;
  93
  94        if (!capable(CAP_SYS_ADMIN) ||
  95            security_locked_down(LOCKDOWN_PCI_ACCESS))
  96                return -EPERM;
  97
  98        dev = pci_get_domain_bus_and_slot(0, bus, dfn);
  99        if (!dev)
 100                return -ENODEV;
 101
 102        switch (len) {
 103        case 1:
 104                err = get_user(byte, (u8 __user *)buf);
 105                if (err)
 106                        break;
 107                err = pci_user_write_config_byte(dev, off, byte);
 108                if (err)
 109                        err = -EIO;
 110                break;
 111
 112        case 2:
 113                err = get_user(word, (u16 __user *)buf);
 114                if (err)
 115                        break;
 116                err = pci_user_write_config_word(dev, off, word);
 117                if (err)
 118                        err = -EIO;
 119                break;
 120
 121        case 4:
 122                err = get_user(dword, (u32 __user *)buf);
 123                if (err)
 124                        break;
 125                err = pci_user_write_config_dword(dev, off, dword);
 126                if (err)
 127                        err = -EIO;
 128                break;
 129
 130        default:
 131                err = -EINVAL;
 132                break;
 133        }
 134        pci_dev_put(dev);
 135        return err;
 136}
 137