pci_user.c revision 172394
1125388Sdes/*-
2125388Sdes * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3125388Sdes * All rights reserved.
4125388Sdes *
5125388Sdes * Redistribution and use in source and binary forms, with or without
6125388Sdes * modification, are permitted provided that the following conditions
7125498Sdes * are met:
8126744Spjd * 1. Redistributions of source code must retain the above copyright
9125388Sdes *    notice unmodified, this list of conditions, and the following
10125388Sdes *    disclaimer.
11125388Sdes * 2. Redistributions in binary form must reproduce the above copyright
12125388Sdes *    notice, this list of conditions and the following disclaimer in the
13125388Sdes *    documentation and/or other materials provided with the distribution.
14125388Sdes *
15125388Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16125388Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17125388Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18125388Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19133987Sthomas * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20133987Sthomas * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21133987Sthomas * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22133987Sthomas * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23125388Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24125388Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25125388Sdes */
26125388Sdes
27125388Sdes#include <sys/cdefs.h>
28125388Sdes__FBSDID("$FreeBSD: head/sys/dev/pci/pci_user.c 172394 2007-09-30 11:05:18Z marius $");
29125388Sdes
30125388Sdes#include "opt_bus.h"	/* XXX trim includes */
31125447Sdes#include "opt_compat.h"
32125447Sdes
33133987Sthomas#include <sys/param.h>
34125388Sdes#include <sys/systm.h>
35125388Sdes#include <sys/malloc.h>
36125388Sdes#include <sys/module.h>
37125388Sdes#include <sys/linker.h>
38125388Sdes#include <sys/fcntl.h>
39133987Sthomas#include <sys/conf.h>
40133987Sthomas#include <sys/kernel.h>
41133987Sthomas#include <sys/proc.h>
42133987Sthomas#include <sys/queue.h>
43125388Sdes#include <sys/types.h>
44125388Sdes
45125388Sdes#include <vm/vm.h>
46125388Sdes#include <vm/pmap.h>
47125388Sdes#include <vm/vm_extern.h>
48125388Sdes
49125388Sdes#include <sys/bus.h>
50125388Sdes#include <machine/bus.h>
51133987Sthomas#include <sys/rman.h>
52125388Sdes#include <machine/resource.h>
53125388Sdes
54125388Sdes#include <sys/pciio.h>
55125388Sdes#include <dev/pci/pcireg.h>
56#include <dev/pci/pcivar.h>
57
58#include "pcib_if.h"
59#include "pci_if.h"
60
61/*
62 * This is the user interface to PCI configuration space.
63 */
64
65static d_open_t 	pci_open;
66static d_close_t	pci_close;
67static int	pci_conf_match(struct pci_match_conf *matches, int num_matches,
68			       struct pci_conf *match_buf);
69static d_ioctl_t	pci_ioctl;
70
71struct cdevsw pcicdev = {
72	.d_version =	D_VERSION,
73	.d_flags =	D_NEEDGIANT,
74	.d_open =	pci_open,
75	.d_close =	pci_close,
76	.d_ioctl =	pci_ioctl,
77	.d_name =	"pci",
78};
79
80static int
81pci_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
82{
83	int error;
84
85	if (oflags & FWRITE) {
86		error = securelevel_gt(td->td_ucred, 0);
87		if (error)
88			return (error);
89	}
90
91	return (0);
92}
93
94static int
95pci_close(struct cdev *dev, int flag, int devtype, struct thread *td)
96{
97	return 0;
98}
99
100/*
101 * Match a single pci_conf structure against an array of pci_match_conf
102 * structures.  The first argument, 'matches', is an array of num_matches
103 * pci_match_conf structures.  match_buf is a pointer to the pci_conf
104 * structure that will be compared to every entry in the matches array.
105 * This function returns 1 on failure, 0 on success.
106 */
107static int
108pci_conf_match(struct pci_match_conf *matches, int num_matches,
109	       struct pci_conf *match_buf)
110{
111	int i;
112
113	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
114		return(1);
115
116	for (i = 0; i < num_matches; i++) {
117		/*
118		 * I'm not sure why someone would do this...but...
119		 */
120		if (matches[i].flags == PCI_GETCONF_NO_MATCH)
121			continue;
122
123		/*
124		 * Look at each of the match flags.  If it's set, do the
125		 * comparison.  If the comparison fails, we don't have a
126		 * match, go on to the next item if there is one.
127		 */
128		if (((matches[i].flags & PCI_GETCONF_MATCH_DOMAIN) != 0)
129		 && (match_buf->pc_sel.pc_domain !=
130		 matches[i].pc_sel.pc_domain))
131			continue;
132
133		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0)
134		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
135			continue;
136
137		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0)
138		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
139			continue;
140
141		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0)
142		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
143			continue;
144
145		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0)
146		 && (match_buf->pc_vendor != matches[i].pc_vendor))
147			continue;
148
149		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0)
150		 && (match_buf->pc_device != matches[i].pc_device))
151			continue;
152
153		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0)
154		 && (match_buf->pc_class != matches[i].pc_class))
155			continue;
156
157		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0)
158		 && (match_buf->pd_unit != matches[i].pd_unit))
159			continue;
160
161		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0)
162		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
163			     sizeof(match_buf->pd_name)) != 0))
164			continue;
165
166		return(0);
167	}
168
169	return(1);
170}
171
172static int
173pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
174{
175	device_t pcidev;
176	struct pci_io *io;
177	const char *name;
178	int error;
179
180	if (!(flag & FWRITE) && cmd != PCIOCGETCONF)
181		return EPERM;
182
183	switch(cmd) {
184	case PCIOCGETCONF:
185		{
186		struct pci_devinfo *dinfo;
187		struct pci_conf_io *cio;
188		struct devlist *devlist_head;
189		struct pci_match_conf *pattern_buf;
190		int num_patterns;
191		size_t iolen;
192		int ionum, i;
193
194		cio = (struct pci_conf_io *)data;
195
196		num_patterns = 0;
197		dinfo = NULL;
198
199		/*
200		 * If the user specified an offset into the device list,
201		 * but the list has changed since they last called this
202		 * ioctl, tell them that the list has changed.  They will
203		 * have to get the list from the beginning.
204		 */
205		if ((cio->offset != 0)
206		 && (cio->generation != pci_generation)){
207			cio->num_matches = 0;
208			cio->status = PCI_GETCONF_LIST_CHANGED;
209			error = 0;
210			break;
211		}
212
213		/*
214		 * Check to see whether the user has asked for an offset
215		 * past the end of our list.
216		 */
217		if (cio->offset >= pci_numdevs) {
218			cio->num_matches = 0;
219			cio->status = PCI_GETCONF_LAST_DEVICE;
220			error = 0;
221			break;
222		}
223
224		/* get the head of the device queue */
225		devlist_head = &pci_devq;
226
227		/*
228		 * Determine how much room we have for pci_conf structures.
229		 * Round the user's buffer size down to the nearest
230		 * multiple of sizeof(struct pci_conf) in case the user
231		 * didn't specify a multiple of that size.
232		 */
233		iolen = min(cio->match_buf_len -
234			    (cio->match_buf_len % sizeof(struct pci_conf)),
235			    pci_numdevs * sizeof(struct pci_conf));
236
237		/*
238		 * Since we know that iolen is a multiple of the size of
239		 * the pciconf union, it's okay to do this.
240		 */
241		ionum = iolen / sizeof(struct pci_conf);
242
243		/*
244		 * If this test is true, the user wants the pci_conf
245		 * structures returned to match the supplied entries.
246		 */
247		if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs)
248		 && (cio->pat_buf_len > 0)) {
249			/*
250			 * pat_buf_len needs to be:
251			 * num_patterns * sizeof(struct pci_match_conf)
252			 * While it is certainly possible the user just
253			 * allocated a large buffer, but set the number of
254			 * matches correctly, it is far more likely that
255			 * their kernel doesn't match the userland utility
256			 * they're using.  It's also possible that the user
257			 * forgot to initialize some variables.  Yes, this
258			 * may be overly picky, but I hazard to guess that
259			 * it's far more likely to just catch folks that
260			 * updated their kernel but not their userland.
261			 */
262			if ((cio->num_patterns *
263			    sizeof(struct pci_match_conf)) != cio->pat_buf_len){
264				/* The user made a mistake, return an error*/
265				cio->status = PCI_GETCONF_ERROR;
266				cio->num_matches = 0;
267				error = EINVAL;
268				break;
269			}
270
271			/*
272			 * Allocate a buffer to hold the patterns.
273			 */
274			pattern_buf = malloc(cio->pat_buf_len, M_TEMP,
275					     M_WAITOK);
276			error = copyin(cio->patterns, pattern_buf,
277				       cio->pat_buf_len);
278			if (error != 0) {
279				error = EINVAL;
280				goto getconfexit;
281			}
282			num_patterns = cio->num_patterns;
283
284		} else if ((cio->num_patterns > 0)
285			|| (cio->pat_buf_len > 0)) {
286			/*
287			 * The user made a mistake, spit out an error.
288			 */
289			cio->status = PCI_GETCONF_ERROR;
290			cio->num_matches = 0;
291			error = EINVAL;
292			break;
293		} else
294			pattern_buf = NULL;
295
296		/*
297		 * Go through the list of devices and copy out the devices
298		 * that match the user's criteria.
299		 */
300		for (cio->num_matches = 0, error = 0, i = 0,
301		     dinfo = STAILQ_FIRST(devlist_head);
302		     (dinfo != NULL) && (cio->num_matches < ionum)
303		     && (error == 0) && (i < pci_numdevs) && (dinfo != NULL);
304		     dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
305
306			if (i < cio->offset)
307				continue;
308
309			/* Populate pd_name and pd_unit */
310			name = NULL;
311			if (dinfo->cfg.dev && dinfo->conf.pd_name[0] == '\0')
312				name = device_get_name(dinfo->cfg.dev);
313			if (name) {
314				strncpy(dinfo->conf.pd_name, name,
315					sizeof(dinfo->conf.pd_name));
316				dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0;
317				dinfo->conf.pd_unit =
318					device_get_unit(dinfo->cfg.dev);
319			}
320
321			if ((pattern_buf == NULL) ||
322			    (pci_conf_match(pattern_buf, num_patterns,
323					    &dinfo->conf) == 0)) {
324
325				/*
326				 * If we've filled up the user's buffer,
327				 * break out at this point.  Since we've
328				 * got a match here, we'll pick right back
329				 * up at the matching entry.  We can also
330				 * tell the user that there are more matches
331				 * left.
332				 */
333				if (cio->num_matches >= ionum)
334					break;
335
336				/* only if can copy it out do we count it */
337				if (!(error = copyout(&dinfo->conf,
338				    &cio->matches[cio->num_matches],
339				    sizeof(struct pci_conf)))) {
340					cio->num_matches++;
341				}
342			}
343		}
344
345		/*
346		 * Set the pointer into the list, so if the user is getting
347		 * n records at a time, where n < pci_numdevs,
348		 */
349		cio->offset = i;
350
351		/*
352		 * Set the generation, the user will need this if they make
353		 * another ioctl call with offset != 0.
354		 */
355		cio->generation = pci_generation;
356
357		/*
358		 * If this is the last device, inform the user so he won't
359		 * bother asking for more devices.  If dinfo isn't NULL, we
360		 * know that there are more matches in the list because of
361		 * the way the traversal is done.
362		 */
363		if (dinfo == NULL)
364			cio->status = PCI_GETCONF_LAST_DEVICE;
365		else
366			cio->status = PCI_GETCONF_MORE_DEVS;
367
368getconfexit:
369		if (pattern_buf != NULL)
370			free(pattern_buf, M_TEMP);
371
372		break;
373		}
374
375	case PCIOCREAD:
376	case PCIOCWRITE:
377		io = (struct pci_io *)data;
378		switch(io->pi_width) {
379		case 4:
380		case 2:
381		case 1:
382			/* Make sure register is in bounds and aligned. */
383			if ((cmd == PCIOCREAD || cmd == PCIOCWRITE) &&
384			    (io->pi_reg < 0 ||
385			    io->pi_reg + io->pi_width > PCI_REGMAX + 1 ||
386			    io->pi_reg & (io->pi_width - 1))) {
387				error = EINVAL;
388				break;
389			}
390			/*
391			 * Assume that the user-level bus number is
392			 * in fact the physical PCI bus number.
393			 * Look up the grandparent, i.e. the bridge device,
394			 * so that we can issue configuration space cycles.
395			 */
396			pcidev = pci_find_dbsf(io->pi_sel.pc_domain,
397			    io->pi_sel.pc_bus, io->pi_sel.pc_dev,
398			    io->pi_sel.pc_func);
399			if (pcidev) {
400				device_t busdev, brdev;
401
402				busdev = device_get_parent(pcidev);
403				brdev = device_get_parent(busdev);
404
405				if (cmd == PCIOCWRITE)
406					PCIB_WRITE_CONFIG(brdev,
407							  io->pi_sel.pc_bus,
408							  io->pi_sel.pc_dev,
409							  io->pi_sel.pc_func,
410							  io->pi_reg,
411							  io->pi_data,
412							  io->pi_width);
413				else
414					io->pi_data =
415						PCIB_READ_CONFIG(brdev,
416							  io->pi_sel.pc_bus,
417							  io->pi_sel.pc_dev,
418							  io->pi_sel.pc_func,
419							  io->pi_reg,
420							  io->pi_width);
421				error = 0;
422			} else {
423#ifdef COMPAT_FREEBSD4
424				if (cmd == PCIOCREAD) {
425					io->pi_data = -1;
426					error = 0;
427				} else
428#endif
429					error = ENODEV;
430			}
431			break;
432		default:
433			error = EINVAL;
434			break;
435		}
436		break;
437
438	default:
439		error = ENOTTY;
440		break;
441	}
442
443	return (error);
444}
445