Merge branch 'for-5.13/thrustmaster' into for-linus
[linux-2.6-microblaze.git] / drivers / pci / syscall.c
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
15 SYSCALL_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
66 error:
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
85 SYSCALL_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 }