Blame view

drivers/pci/proc.c 9.92 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
8
   *	Procfs interface for the PCI bus.
   *
   *	Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz>
   */
  
  #include <linux/init.h>
  #include <linux/pci.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
9
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
12
  #include <linux/module.h>
  #include <linux/proc_fs.h>
  #include <linux/seq_file.h>
aa0ac3651   Alexey Dobriyan   Remove capability...
13
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
  #include <asm/uaccess.h>
  #include <asm/byteorder.h>
bc56b9e01   Greg Kroah-Hartman   [PATCH] PCI: Clea...
16
  #include "pci.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
21
22
23
  
  static int proc_initialized;	/* = 0 */
  
  static loff_t
  proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
  {
  	loff_t new = -1;
46cc65a76   Josef Sipek   [PATCH] struct pa...
24
  	struct inode *inode = file->f_path.dentry->d_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25

1b1dcc1b5   Jes Sorensen   [PATCH] mutex sub...
26
  	mutex_lock(&inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  	switch (whence) {
  	case 0:
  		new = off;
  		break;
  	case 1:
  		new = file->f_pos + off;
  		break;
  	case 2:
  		new = inode->i_size + off;
  		break;
  	}
  	if (new < 0 || new > inode->i_size)
  		new = -EINVAL;
  	else
  		file->f_pos = new;
1b1dcc1b5   Jes Sorensen   [PATCH] mutex sub...
42
  	mutex_unlock(&inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
45
46
47
48
  	return new;
  }
  
  static ssize_t
  proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
  {
46cc65a76   Josef Sipek   [PATCH] struct pa...
49
  	const struct inode *ino = file->f_path.dentry->d_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
51
52
53
54
55
56
57
58
59
60
61
  	const struct proc_dir_entry *dp = PDE(ino);
  	struct pci_dev *dev = dp->data;
  	unsigned int pos = *ppos;
  	unsigned int cnt, size;
  
  	/*
  	 * Normal users can read only the standardized portion of the
  	 * configuration space as several chips lock up when trying to read
  	 * undefined locations (think of Intel PIIX4 as a typical example).
  	 */
  
  	if (capable(CAP_SYS_ADMIN))
cd68602f3   David Rientjes   pci: use size sto...
62
  		size = dp->size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
  	else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
  		size = 128;
  	else
  		size = 64;
  
  	if (pos >= size)
  		return 0;
  	if (nbytes >= size)
  		nbytes = size;
  	if (pos + nbytes > size)
  		nbytes = size - pos;
  	cnt = nbytes;
  
  	if (!access_ok(VERIFY_WRITE, buf, cnt))
  		return -EINVAL;
  
  	if ((pos & 1) && cnt) {
  		unsigned char val;
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
81
  		pci_user_read_config_byte(dev, pos, &val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
84
85
86
87
88
89
  		__put_user(val, buf);
  		buf++;
  		pos++;
  		cnt--;
  	}
  
  	if ((pos & 3) && cnt > 2) {
  		unsigned short val;
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
90
  		pci_user_read_config_word(dev, pos, &val);
f17a077e6   Harvey Harrison   PCI: fixup sparse...
91
  		__put_user(cpu_to_le16(val), (__le16 __user *) buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
93
94
95
96
97
98
  		buf += 2;
  		pos += 2;
  		cnt -= 2;
  	}
  
  	while (cnt >= 4) {
  		unsigned int val;
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
99
  		pci_user_read_config_dword(dev, pos, &val);
f17a077e6   Harvey Harrison   PCI: fixup sparse...
100
  		__put_user(cpu_to_le32(val), (__le32 __user *) buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
103
104
105
106
107
  		buf += 4;
  		pos += 4;
  		cnt -= 4;
  	}
  
  	if (cnt >= 2) {
  		unsigned short val;
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
108
  		pci_user_read_config_word(dev, pos, &val);
f17a077e6   Harvey Harrison   PCI: fixup sparse...
109
  		__put_user(cpu_to_le16(val), (__le16 __user *) buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
112
113
114
115
116
  		buf += 2;
  		pos += 2;
  		cnt -= 2;
  	}
  
  	if (cnt) {
  		unsigned char val;
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
117
  		pci_user_read_config_byte(dev, pos, &val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
120
121
122
123
124
125
126
127
128
129
130
  		__put_user(val, buf);
  		buf++;
  		pos++;
  		cnt--;
  	}
  
  	*ppos = pos;
  	return nbytes;
  }
  
  static ssize_t
  proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, loff_t *ppos)
  {
ecb390804   David Rientjes   pci: write file s...
131
  	struct inode *ino = file->f_path.dentry->d_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
134
  	const struct proc_dir_entry *dp = PDE(ino);
  	struct pci_dev *dev = dp->data;
  	int pos = *ppos;
cd68602f3   David Rientjes   pci: use size sto...
135
  	int size = dp->size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
  	int cnt;
  
  	if (pos >= size)
  		return 0;
  	if (nbytes >= size)
  		nbytes = size;
  	if (pos + nbytes > size)
  		nbytes = size - pos;
  	cnt = nbytes;
  
  	if (!access_ok(VERIFY_READ, buf, cnt))
  		return -EINVAL;
  
  	if ((pos & 1) && cnt) {
  		unsigned char val;
  		__get_user(val, buf);
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
152
  		pci_user_write_config_byte(dev, pos, val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
155
156
157
158
  		buf++;
  		pos++;
  		cnt--;
  	}
  
  	if ((pos & 3) && cnt > 2) {
f17a077e6   Harvey Harrison   PCI: fixup sparse...
159
160
  		__le16 val;
  		__get_user(val, (__le16 __user *) buf);
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
161
  		pci_user_write_config_word(dev, pos, le16_to_cpu(val));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
163
164
165
166
167
  		buf += 2;
  		pos += 2;
  		cnt -= 2;
  	}
  
  	while (cnt >= 4) {
f17a077e6   Harvey Harrison   PCI: fixup sparse...
168
169
  		__le32 val;
  		__get_user(val, (__le32 __user *) buf);
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
170
  		pci_user_write_config_dword(dev, pos, le32_to_cpu(val));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
173
174
175
176
  		buf += 4;
  		pos += 4;
  		cnt -= 4;
  	}
  
  	if (cnt >= 2) {
f17a077e6   Harvey Harrison   PCI: fixup sparse...
177
178
  		__le16 val;
  		__get_user(val, (__le16 __user *) buf);
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
179
  		pci_user_write_config_word(dev, pos, le16_to_cpu(val));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
182
183
184
185
186
187
  		buf += 2;
  		pos += 2;
  		cnt -= 2;
  	}
  
  	if (cnt) {
  		unsigned char val;
  		__get_user(val, buf);
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
188
  		pci_user_write_config_byte(dev, pos, val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
190
191
192
193
194
  		buf++;
  		pos++;
  		cnt--;
  	}
  
  	*ppos = pos;
ecb390804   David Rientjes   pci: write file s...
195
  	i_size_write(ino, dp->size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
197
198
199
200
201
202
  	return nbytes;
  }
  
  struct pci_filp_private {
  	enum pci_mmap_state mmap_state;
  	int write_combine;
  };
add771840   Mathieu Segaud   PCI: Convert driv...
203
204
  static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd,
  			       unsigned long arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
  {
add771840   Mathieu Segaud   PCI: Convert driv...
206
  	const struct proc_dir_entry *dp = PDE(file->f_dentry->d_inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  	struct pci_dev *dev = dp->data;
  #ifdef HAVE_PCI_MMAP
  	struct pci_filp_private *fpriv = file->private_data;
  #endif /* HAVE_PCI_MMAP */
  	int ret = 0;
  
  	switch (cmd) {
  	case PCIIOC_CONTROLLER:
  		ret = pci_domain_nr(dev->bus);
  		break;
  
  #ifdef HAVE_PCI_MMAP
  	case PCIIOC_MMAP_IS_IO:
  		fpriv->mmap_state = pci_mmap_io;
  		break;
  
  	case PCIIOC_MMAP_IS_MEM:
  		fpriv->mmap_state = pci_mmap_mem;
  		break;
  
  	case PCIIOC_WRITE_COMBINE:
  		if (arg)
  			fpriv->write_combine = 1;
  		else
  			fpriv->write_combine = 0;
  		break;
  
  #endif /* HAVE_PCI_MMAP */
  
  	default:
  		ret = -EINVAL;
  		break;
  	};
  
  	return ret;
  }
  
  #ifdef HAVE_PCI_MMAP
  static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma)
  {
46cc65a76   Josef Sipek   [PATCH] struct pa...
247
  	struct inode *inode = file->f_path.dentry->d_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
249
250
  	const struct proc_dir_entry *dp = PDE(inode);
  	struct pci_dev *dev = dp->data;
  	struct pci_filp_private *fpriv = file->private_data;
9eff02e20   Jesse Barnes   PCI: check mmap r...
251
  	int i, ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
  
  	if (!capable(CAP_SYS_RAWIO))
  		return -EPERM;
9eff02e20   Jesse Barnes   PCI: check mmap r...
255
256
  	/* Make sure the caller is mapping a real resource for this device */
  	for (i = 0; i < PCI_ROM_RESOURCE; i++) {
3b519e4ea   Martin Wilck   PCI: fix size che...
257
  		if (pci_mmap_fits(dev, i, vma,  PCI_MMAP_PROCFS))
9eff02e20   Jesse Barnes   PCI: check mmap r...
258
259
260
261
262
  			break;
  	}
  
  	if (i >= PCI_ROM_RESOURCE)
  		return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
  	ret = pci_mmap_page_range(dev, vma,
  				  fpriv->mmap_state,
  				  fpriv->write_combine);
  	if (ret < 0)
  		return ret;
  
  	return 0;
  }
  
  static int proc_bus_pci_open(struct inode *inode, struct file *file)
  {
  	struct pci_filp_private *fpriv = kmalloc(sizeof(*fpriv), GFP_KERNEL);
  
  	if (!fpriv)
  		return -ENOMEM;
  
  	fpriv->mmap_state = pci_mmap_io;
  	fpriv->write_combine = 0;
  
  	file->private_data = fpriv;
  
  	return 0;
  }
  
  static int proc_bus_pci_release(struct inode *inode, struct file *file)
  {
  	kfree(file->private_data);
  	file->private_data = NULL;
  
  	return 0;
  }
  #endif /* HAVE_PCI_MMAP */
d54b1fdb1   Arjan van de Ven   [PATCH] mark stru...
295
  static const struct file_operations proc_bus_pci_operations = {
c7705f344   Denis V. Lunev   drivers: use non-...
296
  	.owner		= THIS_MODULE,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
298
299
  	.llseek		= proc_bus_pci_lseek,
  	.read		= proc_bus_pci_read,
  	.write		= proc_bus_pci_write,
add771840   Mathieu Segaud   PCI: Convert driv...
300
  	.unlocked_ioctl	= proc_bus_pci_ioctl,
991f73954   Arnd Bergmann   PCI: kill BKL in ...
301
  	.compat_ioctl	= proc_bus_pci_ioctl,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
303
304
305
306
307
308
309
310
  #ifdef HAVE_PCI_MMAP
  	.open		= proc_bus_pci_open,
  	.release	= proc_bus_pci_release,
  	.mmap		= proc_bus_pci_mmap,
  #ifdef HAVE_ARCH_PCI_GET_UNMAPPED_AREA
  	.get_unmapped_area = get_pci_unmapped_area,
  #endif /* HAVE_ARCH_PCI_GET_UNMAPPED_AREA */
  #endif /* HAVE_PCI_MMAP */
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
  /* iterator */
  static void *pci_seq_start(struct seq_file *m, loff_t *pos)
  {
  	struct pci_dev *dev = NULL;
  	loff_t n = *pos;
  
  	for_each_pci_dev(dev) {
  		if (!n--)
  			break;
  	}
  	return dev;
  }
  
  static void *pci_seq_next(struct seq_file *m, void *v, loff_t *pos)
  {
  	struct pci_dev *dev = v;
  
  	(*pos)++;
  	dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
  	return dev;
  }
  
  static void pci_seq_stop(struct seq_file *m, void *v)
  {
  	if (v) {
  		struct pci_dev *dev = v;
  		pci_dev_put(dev);
  	}
  }
  
  static int show_device(struct seq_file *m, void *v)
  {
  	const struct pci_dev *dev = v;
  	const struct pci_driver *drv;
  	int i;
  
  	if (dev == NULL)
  		return 0;
  
  	drv = pci_dev_driver(dev);
  	seq_printf(m, "%02x%02x\t%04x%04x\t%x",
  			dev->bus->number,
  			dev->devfn,
  			dev->vendor,
  			dev->device,
  			dev->irq);
fde09c6d8   Yu Zhao   PCI: define PCI r...
357
358
359
  
  	/* only print standard and ROM resources to preserve compatibility */
  	for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
e31dd6e45   Greg Kroah-Hartman   [PATCH] 64bit res...
360
  		resource_size_t start, end;
2311b1f2b   Michael Ellerman   [PATCH] PCI: fix-...
361
  		pci_resource_to_user(dev, i, &dev->resource[i], &start, &end);
1396a8c3f   Greg Kroah-Hartman   [PATCH] 64bit res...
362
363
364
  		seq_printf(m, "\t%16llx",
  			(unsigned long long)(start |
  			(dev->resource[i].flags & PCI_REGION_FLAG_MASK)));
2311b1f2b   Michael Ellerman   [PATCH] PCI: fix-...
365
  	}
fde09c6d8   Yu Zhao   PCI: define PCI r...
366
  	for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
e31dd6e45   Greg Kroah-Hartman   [PATCH] 64bit res...
367
  		resource_size_t start, end;
2311b1f2b   Michael Ellerman   [PATCH] PCI: fix-...
368
  		pci_resource_to_user(dev, i, &dev->resource[i], &start, &end);
1396a8c3f   Greg Kroah-Hartman   [PATCH] 64bit res...
369
  		seq_printf(m, "\t%16llx",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370
  			dev->resource[i].start < dev->resource[i].end ?
1396a8c3f   Greg Kroah-Hartman   [PATCH] 64bit res...
371
  			(unsigned long long)(end - start) + 1 : 0);
2311b1f2b   Michael Ellerman   [PATCH] PCI: fix-...
372
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
373
374
375
376
377
378
379
  	seq_putc(m, '\t');
  	if (drv)
  		seq_printf(m, "%s", drv->name);
  	seq_putc(m, '
  ');
  	return 0;
  }
02d90fc34   Jan Engelhardt   PCI: constify fun...
380
  static const struct seq_operations proc_bus_pci_devices_op = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
  	.start	= pci_seq_start,
  	.next	= pci_seq_next,
  	.stop	= pci_seq_stop,
  	.show	= show_device
  };
  
  static struct proc_dir_entry *proc_bus_pci_dir;
  
  int pci_proc_attach_device(struct pci_dev *dev)
  {
  	struct pci_bus *bus = dev->bus;
  	struct proc_dir_entry *e;
  	char name[16];
  
  	if (!proc_initialized)
  		return -EACCES;
  
  	if (!bus->procdir) {
  		if (pci_proc_domain(bus)) {
  			sprintf(name, "%04x:%02x", pci_domain_nr(bus),
  					bus->number);
  		} else {
  			sprintf(name, "%02x", bus->number);
  		}
  		bus->procdir = proc_mkdir(name, proc_bus_pci_dir);
  		if (!bus->procdir)
  			return -ENOMEM;
  	}
  
  	sprintf(name, "%02x.%x", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
c7705f344   Denis V. Lunev   drivers: use non-...
411
412
  	e = proc_create_data(name, S_IFREG | S_IRUGO | S_IWUSR, bus->procdir,
  			     &proc_bus_pci_operations, dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
  	if (!e)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
415
416
417
418
419
420
421
422
423
424
425
  	e->size = dev->cfg_size;
  	dev->procent = e;
  
  	return 0;
  }
  
  int pci_proc_detach_device(struct pci_dev *dev)
  {
  	struct proc_dir_entry *e;
  
  	if ((e = dev->procent)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
426
427
428
429
430
  		remove_proc_entry(e->name, dev->bus->procdir);
  		dev->procent = NULL;
  	}
  	return 0;
  }
54c762fe6   Adrian Bunk   [PATCH] PCI: driv...
431
  #if 0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
  int pci_proc_attach_bus(struct pci_bus* bus)
  {
  	struct proc_dir_entry *de = bus->procdir;
  
  	if (!proc_initialized)
  		return -EACCES;
  
  	if (!de) {
  		char name[16];
  		sprintf(name, "%02x", bus->number);
  		de = bus->procdir = proc_mkdir(name, proc_bus_pci_dir);
  		if (!de)
  			return -ENOMEM;
  	}
  	return 0;
  }
54c762fe6   Adrian Bunk   [PATCH] PCI: driv...
448
  #endif  /*  0  */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
449
450
451
452
453
454
455
456
  
  int pci_proc_detach_bus(struct pci_bus* bus)
  {
  	struct proc_dir_entry *de = bus->procdir;
  	if (de)
  		remove_proc_entry(de->name, proc_bus_pci_dir);
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
458
459
460
  static int proc_bus_pci_dev_open(struct inode *inode, struct file *file)
  {
  	return seq_open(file, &proc_bus_pci_devices_op);
  }
d54b1fdb1   Arjan van de Ven   [PATCH] mark stru...
461
  static const struct file_operations proc_bus_pci_dev_operations = {
c7705f344   Denis V. Lunev   drivers: use non-...
462
  	.owner		= THIS_MODULE,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
464
465
466
467
468
469
470
  	.open		= proc_bus_pci_dev_open,
  	.read		= seq_read,
  	.llseek		= seq_lseek,
  	.release	= seq_release,
  };
  
  static int __init pci_proc_init(void)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
471
  	struct pci_dev *dev = NULL;
9c37066d8   Alexey Dobriyan   proc: remove proc...
472
  	proc_bus_pci_dir = proc_mkdir("bus/pci", NULL);
c7705f344   Denis V. Lunev   drivers: use non-...
473
474
  	proc_create("devices", 0, proc_bus_pci_dir,
  		    &proc_bus_pci_dev_operations);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
  	proc_initialized = 1;
4e344b1cc   Kulikov Vasiliy   PCI: use for_each...
476
  	for_each_pci_dev(dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477
  		pci_proc_attach_device(dev);
4e344b1cc   Kulikov Vasiliy   PCI: use for_each...
478

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
479
480
  	return 0;
  }
eaf611426   Robert P. J. Day   PCI: Replace depr...
481
  device_initcall(pci_proc_init);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482