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