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