pci_user.c revision 91406
1/*
2 * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice unmodified, this list of conditions, and the following
10 *    disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/dev/pci/pci_user.c 91406 2002-02-27 18:32:23Z jhb $
27 *
28 */
29
30#include "opt_bus.h"	/* XXX trim includes */
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/malloc.h>
35#include <sys/module.h>
36#include <sys/linker.h>
37#include <sys/fcntl.h>
38#include <sys/conf.h>
39#include <sys/kernel.h>
40#include <sys/proc.h>
41#include <sys/queue.h>
42#include <sys/types.h>
43
44#include <vm/vm.h>
45#include <vm/pmap.h>
46#include <vm/vm_extern.h>
47
48#include <sys/bus.h>
49#include <machine/bus.h>
50#include <sys/rman.h>
51#include <machine/resource.h>
52
53#include <sys/pciio.h>
54#include <pci/pcireg.h>
55#include <pci/pcivar.h>
56
57#include "pcib_if.h"
58#include "pci_if.h"
59
60/*
61 * This is the user interface to PCI configuration space.
62 */
63
64static d_open_t 	pci_open;
65static d_close_t	pci_close;
66static int	pci_conf_match(struct pci_match_conf *matches, int num_matches,
67			       struct pci_conf *match_buf);
68static d_ioctl_t	pci_ioctl;
69
70#define	PCI_CDEV	78
71
72struct cdevsw pcicdev = {
73	/* open */	pci_open,
74	/* close */	pci_close,
75	/* read */	noread,
76	/* write */	nowrite,
77	/* ioctl */	pci_ioctl,
78	/* poll */	nopoll,
79	/* mmap */	nommap,
80	/* strategy */	nostrategy,
81	/* name */	"pci",
82	/* maj */	PCI_CDEV,
83	/* dump */	nodump,
84	/* psize */	nopsize,
85	/* flags */	0,
86};
87
88static int
89pci_open(dev_t dev, int oflags, int devtype, struct thread *td)
90{
91	int error;
92
93	if (oflags & FWRITE) {
94		error = securelevel_gt(td->td_ucred, 0);
95		if (error)
96			return (error);
97	}
98
99	return (0);
100}
101
102static int
103pci_close(dev_t dev, int flag, int devtype, struct thread *td)
104{
105	return 0;
106}
107
108/*
109 * Match a single pci_conf structure against an array of pci_match_conf
110 * structures.  The first argument, 'matches', is an array of num_matches
111 * pci_match_conf structures.  match_buf is a pointer to the pci_conf
112 * structure that will be compared to every entry in the matches array.
113 * This function returns 1 on failure, 0 on success.
114 */
115static int
116pci_conf_match(struct pci_match_conf *matches, int num_matches,
117	       struct pci_conf *match_buf)
118{
119	int i;
120
121	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
122		return(1);
123
124	for (i = 0; i < num_matches; i++) {
125		/*
126		 * I'm not sure why someone would do this...but...
127		 */
128		if (matches[i].flags == PCI_GETCONF_NO_MATCH)
129			continue;
130
131		/*
132		 * Look at each of the match flags.  If it's set, do the
133		 * comparison.  If the comparison fails, we don't have a
134		 * match, go on to the next item if there is one.
135		 */
136		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0)
137		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
138			continue;
139
140		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0)
141		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
142			continue;
143
144		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0)
145		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
146			continue;
147
148		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0)
149		 && (match_buf->pc_vendor != matches[i].pc_vendor))
150			continue;
151
152		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0)
153		 && (match_buf->pc_device != matches[i].pc_device))
154			continue;
155
156		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0)
157		 && (match_buf->pc_class != matches[i].pc_class))
158			continue;
159
160		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0)
161		 && (match_buf->pd_unit != matches[i].pd_unit))
162			continue;
163
164		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0)
165		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
166			     sizeof(match_buf->pd_name)) != 0))
167			continue;
168
169		return(0);
170	}
171
172	return(1);
173}
174
175static int
176pci_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
177{
178	device_t pci, pcib;
179	struct pci_io *io;
180	const char *name;
181	int error;
182
183	if (!(flag & FWRITE))
184		return EPERM;
185
186
187	switch(cmd) {
188	case PCIOCGETCONF:
189		{
190		struct pci_devinfo *dinfo;
191		struct pci_conf_io *cio;
192		struct devlist *devlist_head;
193		struct pci_match_conf *pattern_buf;
194		int num_patterns;
195		size_t iolen;
196		int ionum, i;
197
198		cio = (struct pci_conf_io *)data;
199
200		num_patterns = 0;
201		dinfo = NULL;
202
203		/*
204		 * Hopefully the user won't pass in a null pointer, but it
205		 * can't hurt to check.
206		 */
207		if (cio == NULL) {
208			error = EINVAL;
209			break;
210		}
211
212		/*
213		 * If the user specified an offset into the device list,
214		 * but the list has changed since they last called this
215		 * ioctl, tell them that the list has changed.  They will
216		 * have to get the list from the beginning.
217		 */
218		if ((cio->offset != 0)
219		 && (cio->generation != pci_generation)){
220			cio->num_matches = 0;
221			cio->status = PCI_GETCONF_LIST_CHANGED;
222			error = 0;
223			break;
224		}
225
226		/*
227		 * Check to see whether the user has asked for an offset
228		 * past the end of our list.
229		 */
230		if (cio->offset >= pci_numdevs) {
231			cio->num_matches = 0;
232			cio->status = PCI_GETCONF_LAST_DEVICE;
233			error = 0;
234			break;
235		}
236
237		/* get the head of the device queue */
238		devlist_head = &pci_devq;
239
240		/*
241		 * Determine how much room we have for pci_conf structures.
242		 * Round the user's buffer size down to the nearest
243		 * multiple of sizeof(struct pci_conf) in case the user
244		 * didn't specify a multiple of that size.
245		 */
246		iolen = min(cio->match_buf_len -
247			    (cio->match_buf_len % sizeof(struct pci_conf)),
248			    pci_numdevs * sizeof(struct pci_conf));
249
250		/*
251		 * Since we know that iolen is a multiple of the size of
252		 * the pciconf union, it's okay to do this.
253		 */
254		ionum = iolen / sizeof(struct pci_conf);
255
256		/*
257		 * If this test is true, the user wants the pci_conf
258		 * structures returned to match the supplied entries.
259		 */
260		if ((cio->num_patterns > 0)
261		 && (cio->pat_buf_len > 0)) {
262			/*
263			 * pat_buf_len needs to be:
264			 * num_patterns * sizeof(struct pci_match_conf)
265			 * While it is certainly possible the user just
266			 * allocated a large buffer, but set the number of
267			 * matches correctly, it is far more likely that
268			 * their kernel doesn't match the userland utility
269			 * they're using.  It's also possible that the user
270			 * forgot to initialize some variables.  Yes, this
271			 * may be overly picky, but I hazard to guess that
272			 * it's far more likely to just catch folks that
273			 * updated their kernel but not their userland.
274			 */
275			if ((cio->num_patterns *
276			    sizeof(struct pci_match_conf)) != cio->pat_buf_len){
277				/* The user made a mistake, return an error*/
278				cio->status = PCI_GETCONF_ERROR;
279				printf("pci_ioctl: pat_buf_len %d != "
280				       "num_patterns (%d) * sizeof(struct "
281				       "pci_match_conf) (%d)\npci_ioctl: "
282				       "pat_buf_len should be = %d\n",
283				       cio->pat_buf_len, cio->num_patterns,
284				       (int)sizeof(struct pci_match_conf),
285				       (int)sizeof(struct pci_match_conf) *
286				       cio->num_patterns);
287				printf("pci_ioctl: do your headers match your "
288				       "kernel?\n");
289				cio->num_matches = 0;
290				error = EINVAL;
291				break;
292			}
293
294			/*
295			 * Check the user's buffer to make sure it's readable.
296			 */
297			if (!useracc((caddr_t)cio->patterns,
298				    cio->pat_buf_len, VM_PROT_READ)) {
299				printf("pci_ioctl: pattern buffer %p, "
300				       "length %u isn't user accessible for"
301				       " READ\n", cio->patterns,
302				       cio->pat_buf_len);
303				error = EACCES;
304				break;
305			}
306			/*
307			 * Allocate a buffer to hold the patterns.
308			 */
309			pattern_buf = malloc(cio->pat_buf_len, M_TEMP,
310					     M_WAITOK);
311			error = copyin(cio->patterns, pattern_buf,
312				       cio->pat_buf_len);
313			if (error != 0)
314				break;
315			num_patterns = cio->num_patterns;
316
317		} else if ((cio->num_patterns > 0)
318			|| (cio->pat_buf_len > 0)) {
319			/*
320			 * The user made a mistake, spit out an error.
321			 */
322			cio->status = PCI_GETCONF_ERROR;
323			cio->num_matches = 0;
324			printf("pci_ioctl: invalid GETCONF arguments\n");
325			error = EINVAL;
326			break;
327		} else
328			pattern_buf = NULL;
329
330		/*
331		 * Make sure we can write to the match buffer.
332		 */
333		if (!useracc((caddr_t)cio->matches,
334			     cio->match_buf_len, VM_PROT_WRITE)) {
335			printf("pci_ioctl: match buffer %p, length %u "
336			       "isn't user accessible for WRITE\n",
337			       cio->matches, cio->match_buf_len);
338			error = EACCES;
339			break;
340		}
341
342		/*
343		 * Go through the list of devices and copy out the devices
344		 * that match the user's criteria.
345		 */
346		for (cio->num_matches = 0, error = 0, i = 0,
347		     dinfo = STAILQ_FIRST(devlist_head);
348		     (dinfo != NULL) && (cio->num_matches < ionum)
349		     && (error == 0) && (i < pci_numdevs);
350		     dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
351
352			if (i < cio->offset)
353				continue;
354
355			/* Populate pd_name and pd_unit */
356			name = NULL;
357			if (dinfo->cfg.dev && dinfo->conf.pd_name[0] == '\0')
358				name = device_get_name(dinfo->cfg.dev);
359			if (name) {
360				strncpy(dinfo->conf.pd_name, name,
361					sizeof(dinfo->conf.pd_name));
362				dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0;
363				dinfo->conf.pd_unit =
364					device_get_unit(dinfo->cfg.dev);
365			}
366
367			if ((pattern_buf == NULL) ||
368			    (pci_conf_match(pattern_buf, num_patterns,
369					    &dinfo->conf) == 0)) {
370
371				/*
372				 * If we've filled up the user's buffer,
373				 * break out at this point.  Since we've
374				 * got a match here, we'll pick right back
375				 * up at the matching entry.  We can also
376				 * tell the user that there are more matches
377				 * left.
378				 */
379				if (cio->num_matches >= ionum)
380					break;
381
382				error = copyout(&dinfo->conf,
383					        &cio->matches[cio->num_matches],
384						sizeof(struct pci_conf));
385				cio->num_matches++;
386			}
387		}
388
389		/*
390		 * Set the pointer into the list, so if the user is getting
391		 * n records at a time, where n < pci_numdevs,
392		 */
393		cio->offset = i;
394
395		/*
396		 * Set the generation, the user will need this if they make
397		 * another ioctl call with offset != 0.
398		 */
399		cio->generation = pci_generation;
400
401		/*
402		 * If this is the last device, inform the user so he won't
403		 * bother asking for more devices.  If dinfo isn't NULL, we
404		 * know that there are more matches in the list because of
405		 * the way the traversal is done.
406		 */
407		if (dinfo == NULL)
408			cio->status = PCI_GETCONF_LAST_DEVICE;
409		else
410			cio->status = PCI_GETCONF_MORE_DEVS;
411
412		if (pattern_buf != NULL)
413			free(pattern_buf, M_TEMP);
414
415		break;
416		}
417	case PCIOCREAD:
418		io = (struct pci_io *)data;
419		switch(io->pi_width) {
420		case 4:
421		case 2:
422		case 1:
423			/*
424			 * Assume that the user-level bus number is
425			 * actually the pciN instance number. We map
426			 * from that to the real pcib+bus combination.
427			 */
428			pci = devclass_get_device(devclass_find("pci"),
429						  io->pi_sel.pc_bus);
430			if (pci) {
431				int b = pcib_get_bus(pci);
432				pcib = device_get_parent(pci);
433				io->pi_data =
434					PCIB_READ_CONFIG(pcib,
435							 b,
436							 io->pi_sel.pc_dev,
437							 io->pi_sel.pc_func,
438							 io->pi_reg,
439							 io->pi_width);
440				error = 0;
441			} else {
442				error = ENODEV;
443			}
444			break;
445		default:
446			error = ENODEV;
447			break;
448		}
449		break;
450
451	case PCIOCWRITE:
452		io = (struct pci_io *)data;
453		switch(io->pi_width) {
454		case 4:
455		case 2:
456		case 1:
457			/*
458			 * Assume that the user-level bus number is
459			 * actually the pciN instance number. We map
460			 * from that to the real pcib+bus combination.
461			 */
462			pci = devclass_get_device(devclass_find("pci"),
463						  io->pi_sel.pc_bus);
464			if (pci) {
465				int b = pcib_get_bus(pci);
466				pcib = device_get_parent(pci);
467				PCIB_WRITE_CONFIG(pcib,
468						  b,
469						  io->pi_sel.pc_dev,
470						  io->pi_sel.pc_func,
471						  io->pi_reg,
472						  io->pi_data,
473						  io->pi_width);
474				error = 0;
475			} else {
476				error = ENODEV;
477			}
478			break;
479		default:
480			error = ENODEV;
481			break;
482		}
483		break;
484
485	default:
486		error = ENOTTY;
487		break;
488	}
489
490	return (error);
491}
492
493