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        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, (unsigned char __user *)buf);
  56                break;
  57        case 2:
  58                err = put_user(word, (unsigned short __user *)buf);
  59                break;
  60        case 4:
  61                err = put_user(dword, (unsigned int __user *)buf);
  62                break;
  63        }
  64        pci_dev_put(dev);
  65        return err;
  66
  67error:
  68        /* ??? XFree86 doesn't even check the return value.  They
  69           just look for 0xffffffff in the output, since that's what
  70           they get instead of a machine check on x86.  */
  71        switch (len) {
  72        case 1:
  73                put_user(-1, (unsigned char __user *)buf);
  74                break;
  75        case 2:
  76                put_user(-1, (unsigned short __user *)buf);
  77                break;
  78        case 4:
  79                put_user(-1, (unsigned int __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