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_version = D_VERSION, 78 .d_flags = D_NEEDGIANT, 79 .d_open = pci_open, 80 .d_close = pci_close, 81 .d_ioctl = pci_ioctl, 82 .d_name = "pci", 83 .d_maj = PCI_CDEV, 84 }; 85 86 static int 87 pci_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 88 { 89 int error; 90 91 if (oflags & FWRITE) { 92 error = securelevel_gt(td->td_ucred, 0); 93 if (error) 94 return (error); 95 } 96 97 return (0); 98 } 99 100 static int 101 pci_close(struct cdev *dev, int flag, int devtype, struct thread *td) 102 { 103 return 0; 104 } 105 106 /* 107 * Match a single pci_conf structure against an array of pci_match_conf 108 * structures. The first argument, 'matches', is an array of num_matches 109 * pci_match_conf structures. match_buf is a pointer to the pci_conf 110 * structure that will be compared to every entry in the matches array. 111 * This function returns 1 on failure, 0 on success. 112 */ 113 static int 114 pci_conf_match(struct pci_match_conf *matches, int num_matches, 115 struct pci_conf *match_buf) 116 { 117 int i; 118 119 if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) 120 return(1); 121 122 for (i = 0; i < num_matches; i++) { 123 /* 124 * I'm not sure why someone would do this...but... 125 */ 126 if (matches[i].flags == PCI_GETCONF_NO_MATCH) 127 continue; 128 129 /* 130 * Look at each of the match flags. If it's set, do the 131 * comparison. If the comparison fails, we don't have a 132 * match, go on to the next item if there is one. 133 */ 134 if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) 135 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) 136 continue; 137 138 if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) 139 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) 140 continue; 141 142 if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) 143 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) 144 continue; 145 146 if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) 147 && (match_buf->pc_vendor != matches[i].pc_vendor)) 148 continue; 149 150 if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) 151 && (match_buf->pc_device != matches[i].pc_device)) 152 continue; 153 154 if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) 155 && (match_buf->pc_class != matches[i].pc_class)) 156 continue; 157 158 if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) 159 && (match_buf->pd_unit != matches[i].pd_unit)) 160 continue; 161 162 if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) 163 && (strncmp(matches[i].pd_name, match_buf->pd_name, 164 sizeof(match_buf->pd_name)) != 0)) 165 continue; 166 167 return(0); 168 } 169 170 return(1); 171 } 172 173 static int 174 pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 175 { 176 device_t pci, pcib; 177 struct pci_io *io; 178 const char *name; 179 int error; 180 181 if (!(flag & FWRITE) && cmd != PCIOCGETCONF) 182 return EPERM; 183 184 switch(cmd) { 185 case PCIOCGETCONF: 186 { 187 struct pci_devinfo *dinfo; 188 struct pci_conf_io *cio; 189 struct devlist *devlist_head; 190 struct pci_match_conf *pattern_buf; 191 int num_patterns; 192 size_t iolen; 193 int ionum, i; 194 195 cio = (struct pci_conf_io *)data; 196 197 num_patterns = 0; 198 dinfo = NULL; 199 200 /* 201 * If the user specified an offset into the device list, 202 * but the list has changed since they last called this 203 * ioctl, tell them that the list has changed. They will 204 * have to get the list from the beginning. 205 */ 206 if ((cio->offset != 0) 207 && (cio->generation != pci_generation)){ 208 cio->num_matches = 0; 209 cio->status = PCI_GETCONF_LIST_CHANGED; 210 error = 0; 211 break; 212 } 213 214 /* 215 * Check to see whether the user has asked for an offset 216 * past the end of our list. 217 */ 218 if (cio->offset >= pci_numdevs) { 219 cio->num_matches = 0; 220 cio->status = PCI_GETCONF_LAST_DEVICE; 221 error = 0; 222 break; 223 } 224 225 /* get the head of the device queue */ 226 devlist_head = &pci_devq; 227 228 /* 229 * Determine how much room we have for pci_conf structures. 230 * Round the user's buffer size down to the nearest 231 * multiple of sizeof(struct pci_conf) in case the user 232 * didn't specify a multiple of that size. 233 */ 234 iolen = min(cio->match_buf_len - 235 (cio->match_buf_len % sizeof(struct pci_conf)), 236 pci_numdevs * sizeof(struct pci_conf)); 237 238 /* 239 * Since we know that iolen is a multiple of the size of 240 * the pciconf union, it's okay to do this. 241 */ 242 ionum = iolen / sizeof(struct pci_conf); 243 244 /* 245 * If this test is true, the user wants the pci_conf 246 * structures returned to match the supplied entries. 247 */ 248 if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs) 249 && (cio->pat_buf_len > 0)) { 250 /* 251 * pat_buf_len needs to be: 252 * num_patterns * sizeof(struct pci_match_conf) 253 * While it is certainly possible the user just 254 * allocated a large buffer, but set the number of 255 * matches correctly, it is far more likely that 256 * their kernel doesn't match the userland utility 257 * they're using. It's also possible that the user 258 * forgot to initialize some variables. Yes, this 259 * may be overly picky, but I hazard to guess that 260 * it's far more likely to just catch folks that 261 * updated their kernel but not their userland. 262 */ 263 if ((cio->num_patterns * 264 sizeof(struct pci_match_conf)) != cio->pat_buf_len){ 265 /* The user made a mistake, return an error*/ 266 cio->status = PCI_GETCONF_ERROR; 267 cio->num_matches = 0; 268 error = EINVAL; 269 break; 270 } 271 272 /* 273 * Allocate a buffer to hold the patterns. 274 */ 275 pattern_buf = malloc(cio->pat_buf_len, M_TEMP, 276 M_WAITOK); 277 error = copyin(cio->patterns, pattern_buf, 278 cio->pat_buf_len); 279 if (error != 0) { 280 error = EINVAL; 281 goto getconfexit; 282 } 283 num_patterns = cio->num_patterns; 284 285 } else if ((cio->num_patterns > 0) 286 || (cio->pat_buf_len > 0)) { 287 /* 288 * The user made a mistake, spit out an error. 289 */ 290 cio->status = PCI_GETCONF_ERROR; 291 cio->num_matches = 0; 292 error = EINVAL; 293 break; 294 } else 295 pattern_buf = NULL; 296 297 /* 298 * Go through the list of devices and copy out the devices 299 * that match the user's criteria. 300 */ 301 for (cio->num_matches = 0, error = 0, i = 0, 302 dinfo = STAILQ_FIRST(devlist_head); 303 (dinfo != NULL) && (cio->num_matches < ionum) 304 && (error == 0) && (i < pci_numdevs) && (dinfo != NULL); 305 dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { 306 307 if (i < cio->offset) 308 continue; 309 310 /* Populate pd_name and pd_unit */ 311 name = NULL; 312 if (dinfo->cfg.dev && dinfo->conf.pd_name[0] == '\0') 313 name = device_get_name(dinfo->cfg.dev); 314 if (name) { 315 strncpy(dinfo->conf.pd_name, name, 316 sizeof(dinfo->conf.pd_name)); 317 dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0; 318 dinfo->conf.pd_unit = 319 device_get_unit(dinfo->cfg.dev); 320 } 321 322 if ((pattern_buf == NULL) || 323 (pci_conf_match(pattern_buf, num_patterns, 324 &dinfo->conf) == 0)) { 325 326 /* 327 * If we've filled up the user's buffer, 328 * break out at this point. Since we've 329 * got a match here, we'll pick right back 330 * up at the matching entry. We can also 331 * tell the user that there are more matches 332 * left. 333 */ 334 if (cio->num_matches >= ionum) 335 break; 336 337 /* only if can copy it out do we count it */ 338 if (!(error = copyout(&dinfo->conf, 339 &cio->matches[cio->num_matches], 340 sizeof(struct pci_conf)))) { 341 cio->num_matches++; 342 } 343 } 344 } 345 346 /* 347 * Set the pointer into the list, so if the user is getting 348 * n records at a time, where n < pci_numdevs, 349 */ 350 cio->offset = i; 351 352 /* 353 * Set the generation, the user will need this if they make 354 * another ioctl call with offset != 0. 355 */ 356 cio->generation = pci_generation; 357 358 /* 359 * If this is the last device, inform the user so he won't 360 * bother asking for more devices. If dinfo isn't NULL, we 361 * know that there are more matches in the list because of 362 * the way the traversal is done. 363 */ 364 if (dinfo == NULL) 365 cio->status = PCI_GETCONF_LAST_DEVICE; 366 else 367 cio->status = PCI_GETCONF_MORE_DEVS; 368 369 getconfexit: 370 if (pattern_buf != NULL) 371 free(pattern_buf, M_TEMP); 372 373 break; 374 } 375 376 case PCIOCREAD: 377 case PCIOCWRITE: 378 io = (struct pci_io *)data; 379 switch(io->pi_width) { 380 case 4: 381 case 2: 382 case 1: 383 /* make sure register is in bounds and aligned */ 384 if (cmd == PCIOCREAD || cmd == PCIOCWRITE) 385 if (io->pi_reg < 0 || 386 io->pi_reg + io->pi_width > PCI_REGMAX || 387 io->pi_reg & (io->pi_width - 1)) 388 error = EINVAL; 389 390 /* 391 * Assume that the user-level bus number is 392 * actually the pciN instance number. We map 393 * from that to the real pcib+bus combination. 394 */ 395 pci = devclass_get_device(devclass_find("pci"), 396 io->pi_sel.pc_bus); 397 if (pci) { 398 int b = pcib_get_bus(pci); 399 pcib = device_get_parent(pci); 400 if (cmd == PCIOCWRITE) 401 PCIB_WRITE_CONFIG(pcib, 402 b, 403 io->pi_sel.pc_dev, 404 io->pi_sel.pc_func, 405 io->pi_reg, 406 io->pi_data, 407 io->pi_width); 408 else 409 io->pi_data = 410 PCIB_READ_CONFIG(pcib, 411 b, 412 io->pi_sel.pc_dev, 413 io->pi_sel.pc_func, 414 io->pi_reg, 415 io->pi_width); 416 error = 0; 417 } else { 418 error = ENODEV; 419 } 420 break; 421 default: 422 error = EINVAL; 423 break; 424 } 425 break; 426 427 default: 428 error = ENOTTY; 429 break; 430 } 431 432 return (error); 433 } 434