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