| 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 | } |