syscall.c 2.7 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
L
Linus Torvalds 已提交
2 3 4 5 6 7 8 9 10 11 12 13
/*
 *	pci_syscall.c
 *
 * 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/syscalls.h>
14
#include <linux/uaccess.h>
15
#include "pci.h"
L
Linus Torvalds 已提交
16

17 18
SYSCALL_DEFINE5(pciconfig_read, unsigned long, bus, unsigned long, dfn,
		unsigned long, off, unsigned long, len, void __user *, buf)
L
Linus Torvalds 已提交
19 20 21 22 23
{
	struct pci_dev *dev;
	u8 byte;
	u16 word;
	u32 dword;
24 25
	long err;
	long cfg_ret;
L
Linus Torvalds 已提交
26 27

	if (!capable(CAP_SYS_ADMIN))
28
		return -EPERM;
L
Linus Torvalds 已提交
29 30

	err = -ENODEV;
31
	dev = pci_get_domain_bus_and_slot(0, bus, dfn);
L
Linus Torvalds 已提交
32 33 34 35 36
	if (!dev)
		goto error;

	switch (len) {
	case 1:
37
		cfg_ret = pci_user_read_config_byte(dev, off, &byte);
L
Linus Torvalds 已提交
38 39
		break;
	case 2:
40
		cfg_ret = pci_user_read_config_word(dev, off, &word);
L
Linus Torvalds 已提交
41 42
		break;
	case 4:
43
		cfg_ret = pci_user_read_config_dword(dev, off, &dword);
L
Linus Torvalds 已提交
44 45 46 47
		break;
	default:
		err = -EINVAL;
		goto error;
48
	}
L
Linus Torvalds 已提交
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

	err = -EIO;
	if (cfg_ret != PCIBIOS_SUCCESSFUL)
		goto error;

	switch (len) {
	case 1:
		err = put_user(byte, (unsigned char __user *)buf);
		break;
	case 2:
		err = put_user(word, (unsigned short __user *)buf);
		break;
	case 4:
		err = put_user(dword, (unsigned int __user *)buf);
		break;
64 65
	}
	pci_dev_put(dev);
L
Linus Torvalds 已提交
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
	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, (unsigned char __user *)buf);
		break;
	case 2:
		put_user(-1, (unsigned short __user *)buf);
		break;
	case 4:
		put_user(-1, (unsigned int __user *)buf);
		break;
82 83
	}
	pci_dev_put(dev);
L
Linus Torvalds 已提交
84 85 86
	return err;
}

87 88
SYSCALL_DEFINE5(pciconfig_write, unsigned long, bus, unsigned long, dfn,
		unsigned long, off, unsigned long, len, void __user *, buf)
L
Linus Torvalds 已提交
89 90 91 92 93 94 95 96 97 98
{
	struct pci_dev *dev;
	u8 byte;
	u16 word;
	u32 dword;
	int err = 0;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;

99
	dev = pci_get_domain_bus_and_slot(0, bus, dfn);
L
Linus Torvalds 已提交
100 101 102
	if (!dev)
		return -ENODEV;

R
Ryan Desfosses 已提交
103
	switch (len) {
L
Linus Torvalds 已提交
104 105 106 107
	case 1:
		err = get_user(byte, (u8 __user *)buf);
		if (err)
			break;
108
		err = pci_user_write_config_byte(dev, off, byte);
L
Linus Torvalds 已提交
109 110 111 112 113 114 115 116
		if (err != PCIBIOS_SUCCESSFUL)
			err = -EIO;
		break;

	case 2:
		err = get_user(word, (u16 __user *)buf);
		if (err)
			break;
117
		err = pci_user_write_config_word(dev, off, word);
L
Linus Torvalds 已提交
118 119 120 121 122 123 124 125
		if (err != PCIBIOS_SUCCESSFUL)
			err = -EIO;
		break;

	case 4:
		err = get_user(dword, (u32 __user *)buf);
		if (err)
			break;
126
		err = pci_user_write_config_dword(dev, off, dword);
L
Linus Torvalds 已提交
127 128 129 130 131 132 133
		if (err != PCIBIOS_SUCCESSFUL)
			err = -EIO;
		break;

	default:
		err = -EINVAL;
		break;
134 135
	}
	pci_dev_put(dev);
L
Linus Torvalds 已提交
136 137
	return err;
}