File: | dev/pci/pci_user.c |
Warning: | line 837, column 19 Copies out a struct with uncleared padding (>= 7 bytes) |
1 | /*- | |||
2 | * Copyright (c) 1997, Stefan Esser <[email protected]> | |||
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 | ||||
27 | #include <sys/cdefs.h> | |||
28 | __FBSDID("$FreeBSD: releng/11.0/sys/dev/pci/pci_user.c 295816 2016-02-19 16:53:21Z se $")__asm__(".ident\t\"" "$FreeBSD: releng/11.0/sys/dev/pci/pci_user.c 295816 2016-02-19 16:53:21Z se $" "\""); | |||
29 | ||||
30 | #include "opt_bus.h" /* XXX trim includes */ | |||
31 | #include "opt_compat.h" | |||
32 | ||||
33 | #include <sys/param.h> | |||
34 | #include <sys/systm.h> | |||
35 | #include <sys/malloc.h> | |||
36 | #include <sys/module.h> | |||
37 | #include <sys/linker.h> | |||
38 | #include <sys/fcntl.h> | |||
39 | #include <sys/conf.h> | |||
40 | #include <sys/kernel.h> | |||
41 | #include <sys/proc.h> | |||
42 | #include <sys/queue.h> | |||
43 | #include <sys/types.h> | |||
44 | ||||
45 | #include <vm/vm.h> | |||
46 | #include <vm/pmap.h> | |||
47 | #include <vm/vm_extern.h> | |||
48 | ||||
49 | #include <sys/bus.h> | |||
50 | #include <machine/bus.h> | |||
51 | #include <sys/rman.h> | |||
52 | #include <machine/resource.h> | |||
53 | ||||
54 | #include <sys/pciio.h> | |||
55 | #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 | ||||
65 | static d_open_t pci_open; | |||
66 | static d_close_t pci_close; | |||
67 | static int pci_conf_match(struct pci_match_conf *matches, int num_matches, | |||
68 | struct pci_conf *match_buf); | |||
69 | static d_ioctl_t pci_ioctl; | |||
70 | ||||
71 | struct cdevsw pcicdev = { | |||
72 | .d_version = D_VERSION0x17122009, | |||
73 | .d_flags = D_NEEDGIANT0x00400000, | |||
74 | .d_open = pci_open, | |||
75 | .d_close = pci_close, | |||
76 | .d_ioctl = pci_ioctl, | |||
77 | .d_name = "pci", | |||
78 | }; | |||
79 | ||||
80 | static int | |||
81 | pci_open(struct cdev *dev, int oflags, int devtype, struct thread *td) | |||
82 | { | |||
83 | int error; | |||
84 | ||||
85 | if (oflags & FWRITE0x0002) { | |||
86 | error = securelevel_gt(td->td_ucred, 0); | |||
87 | if (error) | |||
88 | return (error); | |||
89 | } | |||
90 | ||||
91 | return (0); | |||
92 | } | |||
93 | ||||
94 | static int | |||
95 | pci_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 | */ | |||
107 | static int | |||
108 | pci_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((void *)0)) || (match_buf == NULL((void *)0)) || (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 | ||||
172 | #if defined(COMPAT_FREEBSD41) || defined(COMPAT_FREEBSD51) || \ | |||
173 | defined(COMPAT_FREEBSD61) | |||
174 | #define PRE7_COMPAT | |||
175 | ||||
176 | typedef enum { | |||
177 | PCI_GETCONF_NO_MATCH_OLD = 0x00, | |||
178 | PCI_GETCONF_MATCH_BUS_OLD = 0x01, | |||
179 | PCI_GETCONF_MATCH_DEV_OLD = 0x02, | |||
180 | PCI_GETCONF_MATCH_FUNC_OLD = 0x04, | |||
181 | PCI_GETCONF_MATCH_NAME_OLD = 0x08, | |||
182 | PCI_GETCONF_MATCH_UNIT_OLD = 0x10, | |||
183 | PCI_GETCONF_MATCH_VENDOR_OLD = 0x20, | |||
184 | PCI_GETCONF_MATCH_DEVICE_OLD = 0x40, | |||
185 | PCI_GETCONF_MATCH_CLASS_OLD = 0x80 | |||
186 | } pci_getconf_flags_old; | |||
187 | ||||
188 | struct pcisel_old { | |||
189 | u_int8_t pc_bus; /* bus number */ | |||
190 | u_int8_t pc_dev; /* device on this bus */ | |||
191 | u_int8_t pc_func; /* function on this device */ | |||
192 | }; | |||
193 | ||||
194 | struct pci_conf_old { | |||
195 | struct pcisel_old pc_sel; /* bus+slot+function */ | |||
196 | u_int8_t pc_hdr; /* PCI header type */ | |||
197 | u_int16_t pc_subvendor; /* card vendor ID */ | |||
198 | u_int16_t pc_subdevice; /* card device ID, assigned by | |||
199 | card vendor */ | |||
200 | u_int16_t pc_vendor; /* chip vendor ID */ | |||
201 | u_int16_t pc_device; /* chip device ID, assigned by | |||
202 | chip vendor */ | |||
203 | u_int8_t pc_class; /* chip PCI class */ | |||
204 | u_int8_t pc_subclass; /* chip PCI subclass */ | |||
205 | u_int8_t pc_progif; /* chip PCI programming interface */ | |||
206 | u_int8_t pc_revid; /* chip revision ID */ | |||
207 | char pd_name[PCI_MAXNAMELEN16 + 1]; /* device name */ | |||
208 | u_long pd_unit; /* device unit number */ | |||
209 | }; | |||
210 | ||||
211 | struct pci_match_conf_old { | |||
212 | struct pcisel_old pc_sel; /* bus+slot+function */ | |||
213 | char pd_name[PCI_MAXNAMELEN16 + 1]; /* device name */ | |||
214 | u_long pd_unit; /* Unit number */ | |||
215 | u_int16_t pc_vendor; /* PCI Vendor ID */ | |||
216 | u_int16_t pc_device; /* PCI Device ID */ | |||
217 | u_int8_t pc_class; /* PCI class */ | |||
218 | pci_getconf_flags_old flags; /* Matching expression */ | |||
219 | }; | |||
220 | ||||
221 | struct pci_io_old { | |||
222 | struct pcisel_old pi_sel; /* device to operate on */ | |||
223 | int pi_reg; /* configuration register to examine */ | |||
224 | int pi_width; /* width (in bytes) of read or write */ | |||
225 | u_int32_t pi_data; /* data to write or result of read */ | |||
226 | }; | |||
227 | ||||
228 | #ifdef COMPAT_FREEBSD321 | |||
229 | struct pci_conf_old32 { | |||
230 | struct pcisel_old pc_sel; /* bus+slot+function */ | |||
231 | uint8_t pc_hdr; /* PCI header type */ | |||
232 | uint16_t pc_subvendor; /* card vendor ID */ | |||
233 | uint16_t pc_subdevice; /* card device ID, assigned by | |||
234 | card vendor */ | |||
235 | uint16_t pc_vendor; /* chip vendor ID */ | |||
236 | uint16_t pc_device; /* chip device ID, assigned by | |||
237 | chip vendor */ | |||
238 | uint8_t pc_class; /* chip PCI class */ | |||
239 | uint8_t pc_subclass; /* chip PCI subclass */ | |||
240 | uint8_t pc_progif; /* chip PCI programming interface */ | |||
241 | uint8_t pc_revid; /* chip revision ID */ | |||
242 | char pd_name[PCI_MAXNAMELEN16 + 1]; /* device name */ | |||
243 | uint32_t pd_unit; /* device unit number (u_long) */ | |||
244 | }; | |||
245 | ||||
246 | struct pci_match_conf_old32 { | |||
247 | struct pcisel_old pc_sel; /* bus+slot+function */ | |||
248 | char pd_name[PCI_MAXNAMELEN16 + 1]; /* device name */ | |||
249 | uint32_t pd_unit; /* Unit number (u_long) */ | |||
250 | uint16_t pc_vendor; /* PCI Vendor ID */ | |||
251 | uint16_t pc_device; /* PCI Device ID */ | |||
252 | uint8_t pc_class; /* PCI class */ | |||
253 | pci_getconf_flags_old flags; /* Matching expression */ | |||
254 | }; | |||
255 | ||||
256 | struct pci_conf_io32 { | |||
257 | uint32_t pat_buf_len; /* pattern buffer length */ | |||
258 | uint32_t num_patterns; /* number of patterns */ | |||
259 | uint32_t patterns; /* pattern buffer | |||
260 | (struct pci_match_conf_old32 *) */ | |||
261 | uint32_t match_buf_len; /* match buffer length */ | |||
262 | uint32_t num_matches; /* number of matches returned */ | |||
263 | uint32_t matches; /* match buffer | |||
264 | (struct pci_conf_old32 *) */ | |||
265 | uint32_t offset; /* offset into device list */ | |||
266 | uint32_t generation; /* device list generation */ | |||
267 | pci_getconf_status status; /* request status */ | |||
268 | }; | |||
269 | ||||
270 | #define PCIOCGETCONF_OLD32((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io32)) & ((1 << 13) - 1)) << 16) | ( (('p')) << 8) | ((1)))) _IOWR('p', 1, struct pci_conf_io32)((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io32)) & ((1 << 13) - 1)) << 16) | ( (('p')) << 8) | ((1)))) | |||
271 | #endif /* COMPAT_FREEBSD32 */ | |||
272 | ||||
273 | #define PCIOCGETCONF_OLD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io)) & ((1 << 13) - 1)) << 16) | (( ('p')) << 8) | ((1)))) _IOWR('p', 1, struct pci_conf_io)((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io)) & ((1 << 13) - 1)) << 16) | (( ('p')) << 8) | ((1)))) | |||
274 | #define PCIOCREAD_OLD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_io_old)) & ((1 << 13) - 1)) << 16) | ((( 'p')) << 8) | ((2)))) _IOWR('p', 2, struct pci_io_old)((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_io_old)) & ((1 << 13) - 1)) << 16) | ((( 'p')) << 8) | ((2)))) | |||
275 | #define PCIOCWRITE_OLD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_io_old)) & ((1 << 13) - 1)) << 16) | ((( 'p')) << 8) | ((3)))) _IOWR('p', 3, struct pci_io_old)((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_io_old)) & ((1 << 13) - 1)) << 16) | ((( 'p')) << 8) | ((3)))) | |||
276 | ||||
277 | static int pci_conf_match_old(struct pci_match_conf_old *matches, | |||
278 | int num_matches, struct pci_conf *match_buf); | |||
279 | ||||
280 | static int | |||
281 | pci_conf_match_old(struct pci_match_conf_old *matches, int num_matches, | |||
282 | struct pci_conf *match_buf) | |||
283 | { | |||
284 | int i; | |||
285 | ||||
286 | if ((matches == NULL((void *)0)) || (match_buf == NULL((void *)0)) || (num_matches <= 0)) | |||
287 | return(1); | |||
288 | ||||
289 | for (i = 0; i < num_matches; i++) { | |||
290 | if (match_buf->pc_sel.pc_domain != 0) | |||
291 | continue; | |||
292 | ||||
293 | /* | |||
294 | * I'm not sure why someone would do this...but... | |||
295 | */ | |||
296 | if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD) | |||
297 | continue; | |||
298 | ||||
299 | /* | |||
300 | * Look at each of the match flags. If it's set, do the | |||
301 | * comparison. If the comparison fails, we don't have a | |||
302 | * match, go on to the next item if there is one. | |||
303 | */ | |||
304 | if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0) | |||
305 | && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) | |||
306 | continue; | |||
307 | ||||
308 | if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0) | |||
309 | && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) | |||
310 | continue; | |||
311 | ||||
312 | if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0) | |||
313 | && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) | |||
314 | continue; | |||
315 | ||||
316 | if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0) | |||
317 | && (match_buf->pc_vendor != matches[i].pc_vendor)) | |||
318 | continue; | |||
319 | ||||
320 | if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0) | |||
321 | && (match_buf->pc_device != matches[i].pc_device)) | |||
322 | continue; | |||
323 | ||||
324 | if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0) | |||
325 | && (match_buf->pc_class != matches[i].pc_class)) | |||
326 | continue; | |||
327 | ||||
328 | if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0) | |||
329 | && (match_buf->pd_unit != matches[i].pd_unit)) | |||
330 | continue; | |||
331 | ||||
332 | if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0) | |||
333 | && (strncmp(matches[i].pd_name, match_buf->pd_name, | |||
334 | sizeof(match_buf->pd_name)) != 0)) | |||
335 | continue; | |||
336 | ||||
337 | return(0); | |||
338 | } | |||
339 | ||||
340 | return(1); | |||
341 | } | |||
342 | ||||
343 | #ifdef COMPAT_FREEBSD321 | |||
344 | static int | |||
345 | pci_conf_match_old32(struct pci_match_conf_old32 *matches, int num_matches, | |||
346 | struct pci_conf *match_buf) | |||
347 | { | |||
348 | int i; | |||
349 | ||||
350 | if ((matches == NULL((void *)0)) || (match_buf == NULL((void *)0)) || (num_matches <= 0)) | |||
351 | return(1); | |||
352 | ||||
353 | for (i = 0; i < num_matches; i++) { | |||
354 | if (match_buf->pc_sel.pc_domain != 0) | |||
355 | continue; | |||
356 | ||||
357 | /* | |||
358 | * I'm not sure why someone would do this...but... | |||
359 | */ | |||
360 | if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD) | |||
361 | continue; | |||
362 | ||||
363 | /* | |||
364 | * Look at each of the match flags. If it's set, do the | |||
365 | * comparison. If the comparison fails, we don't have a | |||
366 | * match, go on to the next item if there is one. | |||
367 | */ | |||
368 | if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0) && | |||
369 | (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) | |||
370 | continue; | |||
371 | ||||
372 | if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0) && | |||
373 | (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) | |||
374 | continue; | |||
375 | ||||
376 | if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0) && | |||
377 | (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) | |||
378 | continue; | |||
379 | ||||
380 | if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0) && | |||
381 | (match_buf->pc_vendor != matches[i].pc_vendor)) | |||
382 | continue; | |||
383 | ||||
384 | if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0) && | |||
385 | (match_buf->pc_device != matches[i].pc_device)) | |||
386 | continue; | |||
387 | ||||
388 | if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0) && | |||
389 | (match_buf->pc_class != matches[i].pc_class)) | |||
390 | continue; | |||
391 | ||||
392 | if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0) && | |||
393 | ((u_int32_t)match_buf->pd_unit != matches[i].pd_unit)) | |||
394 | continue; | |||
395 | ||||
396 | if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0) && | |||
397 | (strncmp(matches[i].pd_name, match_buf->pd_name, | |||
398 | sizeof(match_buf->pd_name)) != 0)) | |||
399 | continue; | |||
400 | ||||
401 | return (0); | |||
402 | } | |||
403 | ||||
404 | return (1); | |||
405 | } | |||
406 | #endif /* COMPAT_FREEBSD32 */ | |||
407 | #endif /* PRE7_COMPAT */ | |||
408 | ||||
409 | static int | |||
410 | pci_list_vpd(device_t dev, struct pci_list_vpd_io *lvio) | |||
411 | { | |||
412 | struct pci_vpd_element vpd_element, *vpd_user; | |||
413 | struct pcicfg_vpd *vpd; | |||
414 | size_t len; | |||
415 | int error, i; | |||
416 | ||||
417 | vpd = pci_fetch_vpd_list(dev); | |||
418 | if (vpd->vpd_reg == 0 || vpd->vpd_ident == NULL((void *)0)) | |||
419 | return (ENXIO6); | |||
420 | ||||
421 | /* | |||
422 | * Calculate the amount of space needed in the data buffer. An | |||
423 | * identifier element is always present followed by the read-only | |||
424 | * and read-write keywords. | |||
425 | */ | |||
426 | len = sizeof(struct pci_vpd_element) + strlen(vpd->vpd_ident); | |||
427 | for (i = 0; i < vpd->vpd_rocnt; i++) | |||
428 | len += sizeof(struct pci_vpd_element) + vpd->vpd_ros[i].len; | |||
429 | for (i = 0; i < vpd->vpd_wcnt; i++) | |||
430 | len += sizeof(struct pci_vpd_element) + vpd->vpd_w[i].len; | |||
431 | ||||
432 | if (lvio->plvi_len == 0) { | |||
433 | lvio->plvi_len = len; | |||
434 | return (0); | |||
435 | } | |||
436 | if (lvio->plvi_len < len) { | |||
437 | lvio->plvi_len = len; | |||
438 | return (ENOMEM12); | |||
439 | } | |||
440 | ||||
441 | /* | |||
442 | * Copyout the identifier string followed by each keyword and | |||
443 | * value. | |||
444 | */ | |||
445 | vpd_user = lvio->plvi_data; | |||
446 | vpd_element.pve_keyword[0] = '\0'; | |||
447 | vpd_element.pve_keyword[1] = '\0'; | |||
448 | vpd_element.pve_flags = PVE_FLAG_IDENT0x01; | |||
449 | vpd_element.pve_datalen = strlen(vpd->vpd_ident); | |||
450 | error = copyout(&vpd_element, vpd_user, sizeof(vpd_element)); | |||
451 | if (error) | |||
452 | return (error); | |||
453 | error = copyout(vpd->vpd_ident, vpd_user->pve_data, | |||
454 | strlen(vpd->vpd_ident)); | |||
455 | if (error) | |||
456 | return (error); | |||
457 | vpd_user = PVE_NEXT(vpd_user)((struct pci_vpd_element *)((char *)(vpd_user) + sizeof(struct pci_vpd_element) + (vpd_user)->pve_datalen)); | |||
458 | vpd_element.pve_flags = 0; | |||
459 | for (i = 0; i < vpd->vpd_rocnt; i++) { | |||
460 | vpd_element.pve_keyword[0] = vpd->vpd_ros[i].keyword[0]; | |||
461 | vpd_element.pve_keyword[1] = vpd->vpd_ros[i].keyword[1]; | |||
462 | vpd_element.pve_datalen = vpd->vpd_ros[i].len; | |||
463 | error = copyout(&vpd_element, vpd_user, sizeof(vpd_element)); | |||
464 | if (error) | |||
465 | return (error); | |||
466 | error = copyout(vpd->vpd_ros[i].value, vpd_user->pve_data, | |||
467 | vpd->vpd_ros[i].len); | |||
468 | if (error) | |||
469 | return (error); | |||
470 | vpd_user = PVE_NEXT(vpd_user)((struct pci_vpd_element *)((char *)(vpd_user) + sizeof(struct pci_vpd_element) + (vpd_user)->pve_datalen)); | |||
471 | } | |||
472 | vpd_element.pve_flags = PVE_FLAG_RW0x02; | |||
473 | for (i = 0; i < vpd->vpd_wcnt; i++) { | |||
474 | vpd_element.pve_keyword[0] = vpd->vpd_w[i].keyword[0]; | |||
475 | vpd_element.pve_keyword[1] = vpd->vpd_w[i].keyword[1]; | |||
476 | vpd_element.pve_datalen = vpd->vpd_w[i].len; | |||
477 | error = copyout(&vpd_element, vpd_user, sizeof(vpd_element)); | |||
478 | if (error) | |||
479 | return (error); | |||
480 | error = copyout(vpd->vpd_w[i].value, vpd_user->pve_data, | |||
481 | vpd->vpd_w[i].len); | |||
482 | if (error) | |||
483 | return (error); | |||
484 | vpd_user = PVE_NEXT(vpd_user)((struct pci_vpd_element *)((char *)(vpd_user) + sizeof(struct pci_vpd_element) + (vpd_user)->pve_datalen)); | |||
485 | } | |||
486 | KASSERT((char *)vpd_user - (char *)lvio->plvi_data == len,do { } while (0) | |||
487 | ("length mismatch"))do { } while (0); | |||
488 | lvio->plvi_len = len; | |||
489 | return (0); | |||
490 | } | |||
491 | ||||
492 | static int | |||
493 | pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) | |||
494 | { | |||
495 | device_t pcidev; | |||
496 | void *confdata; | |||
497 | const char *name; | |||
498 | struct devlist *devlist_head; | |||
499 | struct pci_conf_io *cio = NULL((void *)0); | |||
500 | struct pci_devinfo *dinfo; | |||
501 | struct pci_io *io; | |||
502 | struct pci_bar_io *bio; | |||
503 | struct pci_list_vpd_io *lvio; | |||
504 | struct pci_match_conf *pattern_buf; | |||
505 | struct pci_map *pm; | |||
506 | size_t confsz, iolen, pbufsz; | |||
507 | int error, ionum, i, num_patterns; | |||
508 | #ifdef PRE7_COMPAT | |||
509 | #ifdef COMPAT_FREEBSD321 | |||
510 | struct pci_conf_io32 *cio32 = NULL((void *)0); | |||
511 | struct pci_conf_old32 conf_old32; | |||
512 | struct pci_match_conf_old32 *pattern_buf_old32 = NULL((void *)0); | |||
513 | #endif | |||
514 | struct pci_conf_old conf_old; | |||
515 | struct pci_io iodata; | |||
516 | struct pci_io_old *io_old; | |||
517 | struct pci_match_conf_old *pattern_buf_old = NULL((void *)0); | |||
518 | ||||
519 | io_old = NULL((void *)0); | |||
520 | #endif | |||
521 | ||||
522 | if (!(flag & FWRITE0x0002)) { | |||
| ||||
523 | switch (cmd) { | |||
524 | #ifdef PRE7_COMPAT | |||
525 | #ifdef COMPAT_FREEBSD321 | |||
526 | case PCIOCGETCONF_OLD32((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io32)) & ((1 << 13) - 1)) << 16) | ( (('p')) << 8) | ((1)))): | |||
527 | #endif | |||
528 | case PCIOCGETCONF_OLD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io)) & ((1 << 13) - 1)) << 16) | (( ('p')) << 8) | ((1)))): | |||
529 | #endif | |||
530 | case PCIOCGETCONF((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io)) & ((1 << 13) - 1)) << 16) | (( ('p')) << 8) | ((5)))): | |||
531 | case PCIOCGETBAR((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_bar_io)) & ((1 << 13) - 1)) << 16) | ((( 'p')) << 8) | ((6)))): | |||
532 | case PCIOCLISTVPD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_list_vpd_io)) & ((1 << 13) - 1)) << 16) | ((('p')) << 8) | ((7)))): | |||
533 | break; | |||
534 | default: | |||
535 | return (EPERM1); | |||
536 | } | |||
537 | } | |||
538 | ||||
539 | switch (cmd) { | |||
540 | #ifdef PRE7_COMPAT | |||
541 | #ifdef COMPAT_FREEBSD321 | |||
542 | case PCIOCGETCONF_OLD32((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io32)) & ((1 << 13) - 1)) << 16) | ( (('p')) << 8) | ((1)))): | |||
543 | cio32 = (struct pci_conf_io32 *)data; | |||
544 | cio = malloc(sizeof(struct pci_conf_io), M_TEMP, M_WAITOK0x0002); | |||
545 | cio->pat_buf_len = cio32->pat_buf_len; | |||
546 | cio->num_patterns = cio32->num_patterns; | |||
547 | cio->patterns = (void *)(uintptr_t)cio32->patterns; | |||
548 | cio->match_buf_len = cio32->match_buf_len; | |||
549 | cio->num_matches = cio32->num_matches; | |||
550 | cio->matches = (void *)(uintptr_t)cio32->matches; | |||
551 | cio->offset = cio32->offset; | |||
552 | cio->generation = cio32->generation; | |||
553 | cio->status = cio32->status; | |||
554 | cio32->num_matches = 0; | |||
555 | break; | |||
556 | #endif | |||
557 | case PCIOCGETCONF_OLD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io)) & ((1 << 13) - 1)) << 16) | (( ('p')) << 8) | ((1)))): | |||
558 | #endif | |||
559 | case PCIOCGETCONF((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io)) & ((1 << 13) - 1)) << 16) | (( ('p')) << 8) | ((5)))): | |||
560 | cio = (struct pci_conf_io *)data; | |||
561 | } | |||
562 | ||||
563 | switch (cmd) { | |||
564 | #ifdef PRE7_COMPAT | |||
565 | #ifdef COMPAT_FREEBSD321 | |||
566 | case PCIOCGETCONF_OLD32((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io32)) & ((1 << 13) - 1)) << 16) | ( (('p')) << 8) | ((1)))): | |||
567 | #endif | |||
568 | case PCIOCGETCONF_OLD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io)) & ((1 << 13) - 1)) << 16) | (( ('p')) << 8) | ((1)))): | |||
569 | #endif | |||
570 | case PCIOCGETCONF((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io)) & ((1 << 13) - 1)) << 16) | (( ('p')) << 8) | ((5)))): | |||
571 | ||||
572 | pattern_buf = NULL((void *)0); | |||
573 | num_patterns = 0; | |||
574 | dinfo = NULL((void *)0); | |||
575 | ||||
576 | cio->num_matches = 0; | |||
577 | ||||
578 | /* | |||
579 | * If the user specified an offset into the device list, | |||
580 | * but the list has changed since they last called this | |||
581 | * ioctl, tell them that the list has changed. They will | |||
582 | * have to get the list from the beginning. | |||
583 | */ | |||
584 | if ((cio->offset != 0) | |||
585 | && (cio->generation != pci_generation)){ | |||
586 | cio->status = PCI_GETCONF_LIST_CHANGED; | |||
587 | error = 0; | |||
588 | goto getconfexit; | |||
589 | } | |||
590 | ||||
591 | /* | |||
592 | * Check to see whether the user has asked for an offset | |||
593 | * past the end of our list. | |||
594 | */ | |||
595 | if (cio->offset >= pci_numdevs) { | |||
596 | cio->status = PCI_GETCONF_LAST_DEVICE; | |||
597 | error = 0; | |||
598 | goto getconfexit; | |||
599 | } | |||
600 | ||||
601 | /* get the head of the device queue */ | |||
602 | devlist_head = &pci_devq; | |||
603 | ||||
604 | /* | |||
605 | * Determine how much room we have for pci_conf structures. | |||
606 | * Round the user's buffer size down to the nearest | |||
607 | * multiple of sizeof(struct pci_conf) in case the user | |||
608 | * didn't specify a multiple of that size. | |||
609 | */ | |||
610 | #ifdef PRE7_COMPAT | |||
611 | #ifdef COMPAT_FREEBSD321 | |||
612 | if (cmd == PCIOCGETCONF_OLD32((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io32)) & ((1 << 13) - 1)) << 16) | ( (('p')) << 8) | ((1))))) | |||
613 | confsz = sizeof(struct pci_conf_old32); | |||
614 | else | |||
615 | #endif | |||
616 | if (cmd == PCIOCGETCONF_OLD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io)) & ((1 << 13) - 1)) << 16) | (( ('p')) << 8) | ((1))))) | |||
617 | confsz = sizeof(struct pci_conf_old); | |||
618 | else | |||
619 | #endif | |||
620 | confsz = sizeof(struct pci_conf); | |||
621 | iolen = min(cio->match_buf_len - (cio->match_buf_len % confsz), | |||
622 | pci_numdevs * confsz); | |||
623 | ||||
624 | /* | |||
625 | * Since we know that iolen is a multiple of the size of | |||
626 | * the pciconf union, it's okay to do this. | |||
627 | */ | |||
628 | ionum = iolen / confsz; | |||
629 | ||||
630 | /* | |||
631 | * If this test is true, the user wants the pci_conf | |||
632 | * structures returned to match the supplied entries. | |||
633 | */ | |||
634 | if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs) | |||
635 | && (cio->pat_buf_len > 0)) { | |||
636 | /* | |||
637 | * pat_buf_len needs to be: | |||
638 | * num_patterns * sizeof(struct pci_match_conf) | |||
639 | * While it is certainly possible the user just | |||
640 | * allocated a large buffer, but set the number of | |||
641 | * matches correctly, it is far more likely that | |||
642 | * their kernel doesn't match the userland utility | |||
643 | * they're using. It's also possible that the user | |||
644 | * forgot to initialize some variables. Yes, this | |||
645 | * may be overly picky, but I hazard to guess that | |||
646 | * it's far more likely to just catch folks that | |||
647 | * updated their kernel but not their userland. | |||
648 | */ | |||
649 | #ifdef PRE7_COMPAT | |||
650 | #ifdef COMPAT_FREEBSD321 | |||
651 | if (cmd == PCIOCGETCONF_OLD32((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io32)) & ((1 << 13) - 1)) << 16) | ( (('p')) << 8) | ((1))))) | |||
652 | pbufsz = sizeof(struct pci_match_conf_old32); | |||
653 | else | |||
654 | #endif | |||
655 | if (cmd == PCIOCGETCONF_OLD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io)) & ((1 << 13) - 1)) << 16) | (( ('p')) << 8) | ((1))))) | |||
656 | pbufsz = sizeof(struct pci_match_conf_old); | |||
657 | else | |||
658 | #endif | |||
659 | pbufsz = sizeof(struct pci_match_conf); | |||
660 | if (cio->num_patterns * pbufsz != cio->pat_buf_len) { | |||
661 | /* The user made a mistake, return an error. */ | |||
662 | cio->status = PCI_GETCONF_ERROR; | |||
663 | error = EINVAL22; | |||
664 | goto getconfexit; | |||
665 | } | |||
666 | ||||
667 | /* | |||
668 | * Allocate a buffer to hold the patterns. | |||
669 | */ | |||
670 | #ifdef PRE7_COMPAT | |||
671 | #ifdef COMPAT_FREEBSD321 | |||
672 | if (cmd == PCIOCGETCONF_OLD32((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io32)) & ((1 << 13) - 1)) << 16) | ( (('p')) << 8) | ((1))))) { | |||
673 | pattern_buf_old32 = malloc(cio->pat_buf_len, | |||
674 | M_TEMP, M_WAITOK0x0002); | |||
675 | error = copyin(cio->patterns, | |||
676 | pattern_buf_old32, cio->pat_buf_len); | |||
677 | } else | |||
678 | #endif /* COMPAT_FREEBSD32 */ | |||
679 | if (cmd == PCIOCGETCONF_OLD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io)) & ((1 << 13) - 1)) << 16) | (( ('p')) << 8) | ((1))))) { | |||
680 | pattern_buf_old = malloc(cio->pat_buf_len, | |||
681 | M_TEMP, M_WAITOK0x0002); | |||
682 | error = copyin(cio->patterns, | |||
683 | pattern_buf_old, cio->pat_buf_len); | |||
684 | } else | |||
685 | #endif /* PRE7_COMPAT */ | |||
686 | { | |||
687 | pattern_buf = malloc(cio->pat_buf_len, M_TEMP, | |||
688 | M_WAITOK0x0002); | |||
689 | error = copyin(cio->patterns, pattern_buf, | |||
690 | cio->pat_buf_len); | |||
691 | } | |||
692 | if (error != 0) { | |||
693 | error = EINVAL22; | |||
694 | goto getconfexit; | |||
695 | } | |||
696 | num_patterns = cio->num_patterns; | |||
697 | } else if ((cio->num_patterns > 0) | |||
698 | || (cio->pat_buf_len > 0)) { | |||
699 | /* | |||
700 | * The user made a mistake, spit out an error. | |||
701 | */ | |||
702 | cio->status = PCI_GETCONF_ERROR; | |||
703 | error = EINVAL22; | |||
704 | goto getconfexit; | |||
705 | } | |||
706 | ||||
707 | /* | |||
708 | * Go through the list of devices and copy out the devices | |||
709 | * that match the user's criteria. | |||
710 | */ | |||
711 | for (cio->num_matches = 0, error = 0, i = 0, | |||
712 | dinfo = STAILQ_FIRST(devlist_head)((devlist_head)->stqh_first); | |||
713 | (dinfo != NULL((void *)0)) && (cio->num_matches < ionum) && | |||
714 | (error == 0) && (i < pci_numdevs); | |||
715 | dinfo = STAILQ_NEXT(dinfo, pci_links)((dinfo)->pci_links.stqe_next), i++) { | |||
716 | ||||
717 | if (i < cio->offset) | |||
718 | continue; | |||
719 | ||||
720 | /* Populate pd_name and pd_unit */ | |||
721 | name = NULL((void *)0); | |||
722 | if (dinfo->cfg.dev) | |||
723 | name = device_get_name(dinfo->cfg.dev); | |||
724 | if (name) { | |||
725 | strncpy(dinfo->conf.pd_name, name, | |||
726 | sizeof(dinfo->conf.pd_name)); | |||
727 | dinfo->conf.pd_name[PCI_MAXNAMELEN16] = 0; | |||
728 | dinfo->conf.pd_unit = | |||
729 | device_get_unit(dinfo->cfg.dev); | |||
730 | } else { | |||
731 | dinfo->conf.pd_name[0] = '\0'; | |||
732 | dinfo->conf.pd_unit = 0; | |||
733 | } | |||
734 | ||||
735 | #ifdef PRE7_COMPAT | |||
736 | if ( | |||
737 | #ifdef COMPAT_FREEBSD321 | |||
738 | (cmd == PCIOCGETCONF_OLD32((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io32)) & ((1 << 13) - 1)) << 16) | ( (('p')) << 8) | ((1)))) && | |||
739 | (pattern_buf_old32 == NULL((void *)0) || | |||
740 | pci_conf_match_old32(pattern_buf_old32, | |||
741 | num_patterns, &dinfo->conf) == 0)) || | |||
742 | #endif | |||
743 | (cmd == PCIOCGETCONF_OLD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io)) & ((1 << 13) - 1)) << 16) | (( ('p')) << 8) | ((1)))) && | |||
744 | (pattern_buf_old == NULL((void *)0) || | |||
745 | pci_conf_match_old(pattern_buf_old, num_patterns, | |||
746 | &dinfo->conf) == 0)) || | |||
747 | (cmd == PCIOCGETCONF((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io)) & ((1 << 13) - 1)) << 16) | (( ('p')) << 8) | ((5)))) && | |||
748 | (pattern_buf == NULL((void *)0) || | |||
749 | pci_conf_match(pattern_buf, num_patterns, | |||
750 | &dinfo->conf) == 0))) { | |||
751 | #else | |||
752 | if (pattern_buf == NULL((void *)0) || | |||
753 | pci_conf_match(pattern_buf, num_patterns, | |||
754 | &dinfo->conf) == 0) { | |||
755 | #endif | |||
756 | /* | |||
757 | * If we've filled up the user's buffer, | |||
758 | * break out at this point. Since we've | |||
759 | * got a match here, we'll pick right back | |||
760 | * up at the matching entry. We can also | |||
761 | * tell the user that there are more matches | |||
762 | * left. | |||
763 | */ | |||
764 | if (cio->num_matches >= ionum) | |||
765 | break; | |||
766 | ||||
767 | #ifdef PRE7_COMPAT | |||
768 | #ifdef COMPAT_FREEBSD321 | |||
769 | if (cmd == PCIOCGETCONF_OLD32((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io32)) & ((1 << 13) - 1)) << 16) | ( (('p')) << 8) | ((1))))) { | |||
770 | conf_old32.pc_sel.pc_bus = | |||
771 | dinfo->conf.pc_sel.pc_bus; | |||
772 | conf_old32.pc_sel.pc_dev = | |||
773 | dinfo->conf.pc_sel.pc_dev; | |||
774 | conf_old32.pc_sel.pc_func = | |||
775 | dinfo->conf.pc_sel.pc_func; | |||
776 | conf_old32.pc_hdr = dinfo->conf.pc_hdr; | |||
777 | conf_old32.pc_subvendor = | |||
778 | dinfo->conf.pc_subvendor; | |||
779 | conf_old32.pc_subdevice = | |||
780 | dinfo->conf.pc_subdevice; | |||
781 | conf_old32.pc_vendor = | |||
782 | dinfo->conf.pc_vendor; | |||
783 | conf_old32.pc_device = | |||
784 | dinfo->conf.pc_device; | |||
785 | conf_old32.pc_class = | |||
786 | dinfo->conf.pc_class; | |||
787 | conf_old32.pc_subclass = | |||
788 | dinfo->conf.pc_subclass; | |||
789 | conf_old32.pc_progif = | |||
790 | dinfo->conf.pc_progif; | |||
791 | conf_old32.pc_revid = | |||
792 | dinfo->conf.pc_revid; | |||
793 | strncpy(conf_old32.pd_name, | |||
794 | dinfo->conf.pd_name, | |||
795 | sizeof(conf_old32.pd_name)); | |||
796 | conf_old32.pd_name[PCI_MAXNAMELEN16] = 0; | |||
797 | conf_old32.pd_unit = | |||
798 | (uint32_t)dinfo->conf.pd_unit; | |||
799 | confdata = &conf_old32; | |||
800 | } else | |||
801 | #endif /* COMPAT_FREEBSD32 */ | |||
802 | if (cmd == PCIOCGETCONF_OLD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io)) & ((1 << 13) - 1)) << 16) | (( ('p')) << 8) | ((1))))) { | |||
803 | conf_old.pc_sel.pc_bus = | |||
804 | dinfo->conf.pc_sel.pc_bus; | |||
805 | conf_old.pc_sel.pc_dev = | |||
806 | dinfo->conf.pc_sel.pc_dev; | |||
807 | conf_old.pc_sel.pc_func = | |||
808 | dinfo->conf.pc_sel.pc_func; | |||
809 | conf_old.pc_hdr = dinfo->conf.pc_hdr; | |||
810 | conf_old.pc_subvendor = | |||
811 | dinfo->conf.pc_subvendor; | |||
812 | conf_old.pc_subdevice = | |||
813 | dinfo->conf.pc_subdevice; | |||
814 | conf_old.pc_vendor = | |||
815 | dinfo->conf.pc_vendor; | |||
816 | conf_old.pc_device = | |||
817 | dinfo->conf.pc_device; | |||
818 | conf_old.pc_class = | |||
819 | dinfo->conf.pc_class; | |||
820 | conf_old.pc_subclass = | |||
821 | dinfo->conf.pc_subclass; | |||
822 | conf_old.pc_progif = | |||
823 | dinfo->conf.pc_progif; | |||
824 | conf_old.pc_revid = | |||
825 | dinfo->conf.pc_revid; | |||
826 | strncpy(conf_old.pd_name, | |||
827 | dinfo->conf.pd_name, | |||
828 | sizeof(conf_old.pd_name)); | |||
829 | conf_old.pd_name[PCI_MAXNAMELEN16] = 0; | |||
830 | conf_old.pd_unit = | |||
831 | dinfo->conf.pd_unit; | |||
832 | confdata = &conf_old; | |||
833 | } else | |||
834 | #endif /* PRE7_COMPAT */ | |||
835 | confdata = &dinfo->conf; | |||
836 | /* Only if we can copy it out do we count it. */ | |||
837 | if (!(error = copyout(confdata, | |||
| ||||
838 | (caddr_t)cio->matches + | |||
839 | confsz * cio->num_matches, confsz))) | |||
840 | cio->num_matches++; | |||
841 | } | |||
842 | } | |||
843 | ||||
844 | /* | |||
845 | * Set the pointer into the list, so if the user is getting | |||
846 | * n records at a time, where n < pci_numdevs, | |||
847 | */ | |||
848 | cio->offset = i; | |||
849 | ||||
850 | /* | |||
851 | * Set the generation, the user will need this if they make | |||
852 | * another ioctl call with offset != 0. | |||
853 | */ | |||
854 | cio->generation = pci_generation; | |||
855 | ||||
856 | /* | |||
857 | * If this is the last device, inform the user so he won't | |||
858 | * bother asking for more devices. If dinfo isn't NULL, we | |||
859 | * know that there are more matches in the list because of | |||
860 | * the way the traversal is done. | |||
861 | */ | |||
862 | if (dinfo == NULL((void *)0)) | |||
863 | cio->status = PCI_GETCONF_LAST_DEVICE; | |||
864 | else | |||
865 | cio->status = PCI_GETCONF_MORE_DEVS; | |||
866 | ||||
867 | getconfexit: | |||
868 | #ifdef PRE7_COMPAT | |||
869 | #ifdef COMPAT_FREEBSD321 | |||
870 | if (cmd == PCIOCGETCONF_OLD32((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_conf_io32)) & ((1 << 13) - 1)) << 16) | ( (('p')) << 8) | ((1))))) { | |||
871 | cio32->status = cio->status; | |||
872 | cio32->generation = cio->generation; | |||
873 | cio32->offset = cio->offset; | |||
874 | cio32->num_matches = cio->num_matches; | |||
875 | free(cio, M_TEMP); | |||
876 | } | |||
877 | if (pattern_buf_old32 != NULL((void *)0)) | |||
878 | free(pattern_buf_old32, M_TEMP); | |||
879 | #endif | |||
880 | if (pattern_buf_old != NULL((void *)0)) | |||
881 | free(pattern_buf_old, M_TEMP); | |||
882 | #endif | |||
883 | if (pattern_buf != NULL((void *)0)) | |||
884 | free(pattern_buf, M_TEMP); | |||
885 | ||||
886 | break; | |||
887 | ||||
888 | #ifdef PRE7_COMPAT | |||
889 | case PCIOCREAD_OLD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_io_old)) & ((1 << 13) - 1)) << 16) | ((( 'p')) << 8) | ((2)))): | |||
890 | case PCIOCWRITE_OLD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_io_old)) & ((1 << 13) - 1)) << 16) | ((( 'p')) << 8) | ((3)))): | |||
891 | io_old = (struct pci_io_old *)data; | |||
892 | iodata.pi_sel.pc_domain = 0; | |||
893 | iodata.pi_sel.pc_bus = io_old->pi_sel.pc_bus; | |||
894 | iodata.pi_sel.pc_dev = io_old->pi_sel.pc_dev; | |||
895 | iodata.pi_sel.pc_func = io_old->pi_sel.pc_func; | |||
896 | iodata.pi_reg = io_old->pi_reg; | |||
897 | iodata.pi_width = io_old->pi_width; | |||
898 | iodata.pi_data = io_old->pi_data; | |||
899 | data = (caddr_t)&iodata; | |||
900 | /* FALLTHROUGH */ | |||
901 | #endif | |||
902 | case PCIOCREAD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_io)) & ((1 << 13) - 1)) << 16) | ((('p') ) << 8) | ((2)))): | |||
903 | case PCIOCWRITE((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_io)) & ((1 << 13) - 1)) << 16) | ((('p') ) << 8) | ((3)))): | |||
904 | io = (struct pci_io *)data; | |||
905 | switch(io->pi_width) { | |||
906 | case 4: | |||
907 | case 2: | |||
908 | case 1: | |||
909 | /* Make sure register is not negative and aligned. */ | |||
910 | if (io->pi_reg < 0 || | |||
911 | io->pi_reg & (io->pi_width - 1)) { | |||
912 | error = EINVAL22; | |||
913 | break; | |||
914 | } | |||
915 | /* | |||
916 | * Assume that the user-level bus number is | |||
917 | * in fact the physical PCI bus number. | |||
918 | * Look up the grandparent, i.e. the bridge device, | |||
919 | * so that we can issue configuration space cycles. | |||
920 | */ | |||
921 | pcidev = pci_find_dbsf(io->pi_sel.pc_domain, | |||
922 | io->pi_sel.pc_bus, io->pi_sel.pc_dev, | |||
923 | io->pi_sel.pc_func); | |||
924 | if (pcidev) { | |||
925 | #ifdef PRE7_COMPAT | |||
926 | if (cmd == PCIOCWRITE((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_io)) & ((1 << 13) - 1)) << 16) | ((('p') ) << 8) | ((3)))) || cmd == PCIOCWRITE_OLD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_io_old)) & ((1 << 13) - 1)) << 16) | ((( 'p')) << 8) | ((3))))) | |||
927 | #else | |||
928 | if (cmd == PCIOCWRITE((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_io)) & ((1 << 13) - 1)) << 16) | ((('p') ) << 8) | ((3))))) | |||
929 | #endif | |||
930 | pci_write_config(pcidev, | |||
931 | io->pi_reg, | |||
932 | io->pi_data, | |||
933 | io->pi_width); | |||
934 | #ifdef PRE7_COMPAT | |||
935 | else if (cmd == PCIOCREAD_OLD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_io_old)) & ((1 << 13) - 1)) << 16) | ((( 'p')) << 8) | ((2))))) | |||
936 | io_old->pi_data = | |||
937 | pci_read_config(pcidev, | |||
938 | io->pi_reg, | |||
939 | io->pi_width); | |||
940 | #endif | |||
941 | else | |||
942 | io->pi_data = | |||
943 | pci_read_config(pcidev, | |||
944 | io->pi_reg, | |||
945 | io->pi_width); | |||
946 | error = 0; | |||
947 | } else { | |||
948 | #ifdef COMPAT_FREEBSD41 | |||
949 | if (cmd == PCIOCREAD_OLD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_io_old)) & ((1 << 13) - 1)) << 16) | ((( 'p')) << 8) | ((2))))) { | |||
950 | io_old->pi_data = -1; | |||
951 | error = 0; | |||
952 | } else | |||
953 | #endif | |||
954 | error = ENODEV19; | |||
955 | } | |||
956 | break; | |||
957 | default: | |||
958 | error = EINVAL22; | |||
959 | break; | |||
960 | } | |||
961 | break; | |||
962 | ||||
963 | case PCIOCGETBAR((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_bar_io)) & ((1 << 13) - 1)) << 16) | ((( 'p')) << 8) | ((6)))): | |||
964 | bio = (struct pci_bar_io *)data; | |||
965 | ||||
966 | /* | |||
967 | * Assume that the user-level bus number is | |||
968 | * in fact the physical PCI bus number. | |||
969 | */ | |||
970 | pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain, | |||
971 | bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev, | |||
972 | bio->pbi_sel.pc_func); | |||
973 | if (pcidev == NULL((void *)0)) { | |||
974 | error = ENODEV19; | |||
975 | break; | |||
976 | } | |||
977 | pm = pci_find_bar(pcidev, bio->pbi_reg); | |||
978 | if (pm == NULL((void *)0)) { | |||
979 | error = EINVAL22; | |||
980 | break; | |||
981 | } | |||
982 | bio->pbi_base = pm->pm_value; | |||
983 | bio->pbi_length = (pci_addr_t)1 << pm->pm_size; | |||
984 | bio->pbi_enabled = pci_bar_enabled(pcidev, pm); | |||
985 | error = 0; | |||
986 | break; | |||
987 | case PCIOCATTACHED((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_io)) & ((1 << 13) - 1)) << 16) | ((('p') ) << 8) | ((4)))): | |||
988 | error = 0; | |||
989 | io = (struct pci_io *)data; | |||
990 | pcidev = pci_find_dbsf(io->pi_sel.pc_domain, io->pi_sel.pc_bus, | |||
991 | io->pi_sel.pc_dev, io->pi_sel.pc_func); | |||
992 | if (pcidev != NULL((void *)0)) | |||
993 | io->pi_data = device_is_attached(pcidev); | |||
994 | else | |||
995 | error = ENODEV19; | |||
996 | break; | |||
997 | case PCIOCLISTVPD((unsigned long) (((0x80000000|0x40000000)) | (((sizeof(struct pci_list_vpd_io)) & ((1 << 13) - 1)) << 16) | ((('p')) << 8) | ((7)))): | |||
998 | lvio = (struct pci_list_vpd_io *)data; | |||
999 | ||||
1000 | /* | |||
1001 | * Assume that the user-level bus number is | |||
1002 | * in fact the physical PCI bus number. | |||
1003 | */ | |||
1004 | pcidev = pci_find_dbsf(lvio->plvi_sel.pc_domain, | |||
1005 | lvio->plvi_sel.pc_bus, lvio->plvi_sel.pc_dev, | |||
1006 | lvio->plvi_sel.pc_func); | |||
1007 | if (pcidev == NULL((void *)0)) { | |||
1008 | error = ENODEV19; | |||
1009 | break; | |||
1010 | } | |||
1011 | error = pci_list_vpd(pcidev, lvio); | |||
1012 | break; | |||
1013 | default: | |||
1014 | error = ENOTTY25; | |||
1015 | break; | |||
1016 | } | |||
1017 | ||||
1018 | return (error); | |||
1019 | } |