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