pci_user.c revision 145022
1119418Sobrien/*- 269953Smsmith * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 369953Smsmith * All rights reserved. 469953Smsmith * 569953Smsmith * Redistribution and use in source and binary forms, with or without 669953Smsmith * modification, are permitted provided that the following conditions 769953Smsmith * are met: 869953Smsmith * 1. Redistributions of source code must retain the above copyright 969953Smsmith * notice unmodified, this list of conditions, and the following 1069953Smsmith * disclaimer. 1169953Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1269953Smsmith * notice, this list of conditions and the following disclaimer in the 1369953Smsmith * documentation and/or other materials provided with the distribution. 1469953Smsmith * 1569953Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1669953Smsmith * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1769953Smsmith * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1869953Smsmith * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1969953Smsmith * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2069953Smsmith * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2169953Smsmith * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2269953Smsmith * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2369953Smsmith * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2469953Smsmith * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2569953Smsmith */ 2669953Smsmith 27119418Sobrien#include <sys/cdefs.h> 28119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/pci/pci_user.c 145022 2005-04-13 17:34:38Z bms $"); 29119418Sobrien 3069953Smsmith#include "opt_bus.h" /* XXX trim includes */ 3169953Smsmith 3269953Smsmith#include <sys/param.h> 3369953Smsmith#include <sys/systm.h> 3469953Smsmith#include <sys/malloc.h> 3569953Smsmith#include <sys/module.h> 3669953Smsmith#include <sys/linker.h> 3769953Smsmith#include <sys/fcntl.h> 3869953Smsmith#include <sys/conf.h> 3969953Smsmith#include <sys/kernel.h> 4083975Srwatson#include <sys/proc.h> 4169953Smsmith#include <sys/queue.h> 4269953Smsmith#include <sys/types.h> 4369953Smsmith 4469953Smsmith#include <vm/vm.h> 4569953Smsmith#include <vm/pmap.h> 4669953Smsmith#include <vm/vm_extern.h> 4769953Smsmith 4869953Smsmith#include <sys/bus.h> 4969953Smsmith#include <machine/bus.h> 5069953Smsmith#include <sys/rman.h> 5169953Smsmith#include <machine/resource.h> 5269953Smsmith 5369953Smsmith#include <sys/pciio.h> 54119285Simp#include <dev/pci/pcireg.h> 55119285Simp#include <dev/pci/pcivar.h> 5669953Smsmith 5769953Smsmith#include "pcib_if.h" 5869953Smsmith#include "pci_if.h" 5969953Smsmith 6069953Smsmith/* 6169953Smsmith * This is the user interface to PCI configuration space. 6269953Smsmith */ 6369953Smsmith 6483366Sjulianstatic d_open_t pci_open; 6583366Sjulianstatic d_close_t pci_close; 6669953Smsmithstatic int pci_conf_match(struct pci_match_conf *matches, int num_matches, 6769953Smsmith struct pci_conf *match_buf); 6883366Sjulianstatic d_ioctl_t pci_ioctl; 6969953Smsmith 7069953Smsmithstruct cdevsw pcicdev = { 71126080Sphk .d_version = D_VERSION, 72126080Sphk .d_flags = D_NEEDGIANT, 73111815Sphk .d_open = pci_open, 74111815Sphk .d_close = pci_close, 75111815Sphk .d_ioctl = pci_ioctl, 76111815Sphk .d_name = "pci", 7769953Smsmith}; 7869953Smsmith 7969953Smsmithstatic int 80130585Sphkpci_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 8169953Smsmith{ 8283975Srwatson int error; 8383975Srwatson 8483975Srwatson if (oflags & FWRITE) { 8591406Sjhb error = securelevel_gt(td->td_ucred, 0); 8683975Srwatson if (error) 8783975Srwatson return (error); 8869953Smsmith } 8983975Srwatson 9083975Srwatson return (0); 9169953Smsmith} 9269953Smsmith 9369953Smsmithstatic int 94130585Sphkpci_close(struct cdev *dev, int flag, int devtype, struct thread *td) 9569953Smsmith{ 9669953Smsmith return 0; 9769953Smsmith} 9869953Smsmith 9969953Smsmith/* 10069953Smsmith * Match a single pci_conf structure against an array of pci_match_conf 10169953Smsmith * structures. The first argument, 'matches', is an array of num_matches 10269953Smsmith * pci_match_conf structures. match_buf is a pointer to the pci_conf 10369953Smsmith * structure that will be compared to every entry in the matches array. 10469953Smsmith * This function returns 1 on failure, 0 on success. 10569953Smsmith */ 10669953Smsmithstatic int 10769953Smsmithpci_conf_match(struct pci_match_conf *matches, int num_matches, 10869953Smsmith struct pci_conf *match_buf) 10969953Smsmith{ 11069953Smsmith int i; 11169953Smsmith 11269953Smsmith if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) 11369953Smsmith return(1); 11469953Smsmith 11569953Smsmith for (i = 0; i < num_matches; i++) { 11669953Smsmith /* 11769953Smsmith * I'm not sure why someone would do this...but... 11869953Smsmith */ 11969953Smsmith if (matches[i].flags == PCI_GETCONF_NO_MATCH) 12069953Smsmith continue; 12169953Smsmith 12269953Smsmith /* 12369953Smsmith * Look at each of the match flags. If it's set, do the 12469953Smsmith * comparison. If the comparison fails, we don't have a 12569953Smsmith * match, go on to the next item if there is one. 12669953Smsmith */ 12769953Smsmith if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) 12869953Smsmith && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) 12969953Smsmith continue; 13069953Smsmith 13169953Smsmith if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) 13269953Smsmith && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) 13369953Smsmith continue; 13469953Smsmith 13569953Smsmith if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) 13669953Smsmith && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) 13769953Smsmith continue; 13869953Smsmith 13969953Smsmith if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) 14069953Smsmith && (match_buf->pc_vendor != matches[i].pc_vendor)) 14169953Smsmith continue; 14269953Smsmith 14369953Smsmith if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) 14469953Smsmith && (match_buf->pc_device != matches[i].pc_device)) 14569953Smsmith continue; 14669953Smsmith 14769953Smsmith if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) 14869953Smsmith && (match_buf->pc_class != matches[i].pc_class)) 14969953Smsmith continue; 15069953Smsmith 15169953Smsmith if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) 15269953Smsmith && (match_buf->pd_unit != matches[i].pd_unit)) 15369953Smsmith continue; 15469953Smsmith 15569953Smsmith if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) 15669953Smsmith && (strncmp(matches[i].pd_name, match_buf->pd_name, 15769953Smsmith sizeof(match_buf->pd_name)) != 0)) 15869953Smsmith continue; 15969953Smsmith 16069953Smsmith return(0); 16169953Smsmith } 16269953Smsmith 16369953Smsmith return(1); 16469953Smsmith} 16569953Smsmith 16669953Smsmithstatic int 167130585Sphkpci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 16869953Smsmith{ 169145022Sbms device_t pcidev; 17069953Smsmith struct pci_io *io; 17169953Smsmith const char *name; 17269953Smsmith int error; 17369953Smsmith 174116702Sjmg if (!(flag & FWRITE) && cmd != PCIOCGETCONF) 17569953Smsmith return EPERM; 17669953Smsmith 17769953Smsmith switch(cmd) { 17869953Smsmith case PCIOCGETCONF: 17969953Smsmith { 18069953Smsmith struct pci_devinfo *dinfo; 18169953Smsmith struct pci_conf_io *cio; 18269953Smsmith struct devlist *devlist_head; 18369953Smsmith struct pci_match_conf *pattern_buf; 18469953Smsmith int num_patterns; 18569953Smsmith size_t iolen; 18669953Smsmith int ionum, i; 18769953Smsmith 18869953Smsmith cio = (struct pci_conf_io *)data; 18969953Smsmith 19069953Smsmith num_patterns = 0; 19169953Smsmith dinfo = NULL; 19269953Smsmith 19369953Smsmith /* 19469953Smsmith * If the user specified an offset into the device list, 19569953Smsmith * but the list has changed since they last called this 19669953Smsmith * ioctl, tell them that the list has changed. They will 19769953Smsmith * have to get the list from the beginning. 19869953Smsmith */ 19969953Smsmith if ((cio->offset != 0) 20069953Smsmith && (cio->generation != pci_generation)){ 20169953Smsmith cio->num_matches = 0; 20269953Smsmith cio->status = PCI_GETCONF_LIST_CHANGED; 20369953Smsmith error = 0; 20469953Smsmith break; 20569953Smsmith } 20669953Smsmith 20769953Smsmith /* 20869953Smsmith * Check to see whether the user has asked for an offset 20969953Smsmith * past the end of our list. 21069953Smsmith */ 21169953Smsmith if (cio->offset >= pci_numdevs) { 21269953Smsmith cio->num_matches = 0; 21369953Smsmith cio->status = PCI_GETCONF_LAST_DEVICE; 21469953Smsmith error = 0; 21569953Smsmith break; 21669953Smsmith } 21769953Smsmith 21869953Smsmith /* get the head of the device queue */ 21969953Smsmith devlist_head = &pci_devq; 22069953Smsmith 22169953Smsmith /* 22269953Smsmith * Determine how much room we have for pci_conf structures. 22369953Smsmith * Round the user's buffer size down to the nearest 22469953Smsmith * multiple of sizeof(struct pci_conf) in case the user 22569953Smsmith * didn't specify a multiple of that size. 22669953Smsmith */ 22769953Smsmith iolen = min(cio->match_buf_len - 22869953Smsmith (cio->match_buf_len % sizeof(struct pci_conf)), 22969953Smsmith pci_numdevs * sizeof(struct pci_conf)); 23069953Smsmith 23169953Smsmith /* 23269953Smsmith * Since we know that iolen is a multiple of the size of 23369953Smsmith * the pciconf union, it's okay to do this. 23469953Smsmith */ 23569953Smsmith ionum = iolen / sizeof(struct pci_conf); 23669953Smsmith 23769953Smsmith /* 23869953Smsmith * If this test is true, the user wants the pci_conf 23969953Smsmith * structures returned to match the supplied entries. 24069953Smsmith */ 241116704Sjmg if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs) 24269953Smsmith && (cio->pat_buf_len > 0)) { 24369953Smsmith /* 24469953Smsmith * pat_buf_len needs to be: 24569953Smsmith * num_patterns * sizeof(struct pci_match_conf) 24669953Smsmith * While it is certainly possible the user just 24769953Smsmith * allocated a large buffer, but set the number of 24869953Smsmith * matches correctly, it is far more likely that 24969953Smsmith * their kernel doesn't match the userland utility 25069953Smsmith * they're using. It's also possible that the user 25169953Smsmith * forgot to initialize some variables. Yes, this 25269953Smsmith * may be overly picky, but I hazard to guess that 25369953Smsmith * it's far more likely to just catch folks that 25469953Smsmith * updated their kernel but not their userland. 25569953Smsmith */ 25669953Smsmith if ((cio->num_patterns * 25769953Smsmith sizeof(struct pci_match_conf)) != cio->pat_buf_len){ 25869953Smsmith /* The user made a mistake, return an error*/ 25969953Smsmith cio->status = PCI_GETCONF_ERROR; 26069953Smsmith cio->num_matches = 0; 26169953Smsmith error = EINVAL; 26269953Smsmith break; 26369953Smsmith } 26469953Smsmith 26569953Smsmith /* 26669953Smsmith * Allocate a buffer to hold the patterns. 26769953Smsmith */ 26869953Smsmith pattern_buf = malloc(cio->pat_buf_len, M_TEMP, 269111119Simp M_WAITOK); 27069953Smsmith error = copyin(cio->patterns, pattern_buf, 27169953Smsmith cio->pat_buf_len); 272116702Sjmg if (error != 0) { 273116702Sjmg error = EINVAL; 274116702Sjmg goto getconfexit; 275116702Sjmg } 27669953Smsmith num_patterns = cio->num_patterns; 27769953Smsmith 27869953Smsmith } else if ((cio->num_patterns > 0) 27969953Smsmith || (cio->pat_buf_len > 0)) { 28069953Smsmith /* 28169953Smsmith * The user made a mistake, spit out an error. 28269953Smsmith */ 28369953Smsmith cio->status = PCI_GETCONF_ERROR; 28469953Smsmith cio->num_matches = 0; 28569953Smsmith error = EINVAL; 28669953Smsmith break; 28769953Smsmith } else 28869953Smsmith pattern_buf = NULL; 28969953Smsmith 29069953Smsmith /* 29169953Smsmith * Go through the list of devices and copy out the devices 29269953Smsmith * that match the user's criteria. 29369953Smsmith */ 29469953Smsmith for (cio->num_matches = 0, error = 0, i = 0, 29569953Smsmith dinfo = STAILQ_FIRST(devlist_head); 29669953Smsmith (dinfo != NULL) && (cio->num_matches < ionum) 297116702Sjmg && (error == 0) && (i < pci_numdevs) && (dinfo != NULL); 29869953Smsmith dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { 29969953Smsmith 30069953Smsmith if (i < cio->offset) 30169953Smsmith continue; 30269953Smsmith 30369953Smsmith /* Populate pd_name and pd_unit */ 30469953Smsmith name = NULL; 30569953Smsmith if (dinfo->cfg.dev && dinfo->conf.pd_name[0] == '\0') 30669953Smsmith name = device_get_name(dinfo->cfg.dev); 30769953Smsmith if (name) { 30869953Smsmith strncpy(dinfo->conf.pd_name, name, 30969953Smsmith sizeof(dinfo->conf.pd_name)); 31069953Smsmith dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0; 31169953Smsmith dinfo->conf.pd_unit = 31269953Smsmith device_get_unit(dinfo->cfg.dev); 31369953Smsmith } 31469953Smsmith 31569953Smsmith if ((pattern_buf == NULL) || 31669953Smsmith (pci_conf_match(pattern_buf, num_patterns, 31769953Smsmith &dinfo->conf) == 0)) { 31869953Smsmith 31969953Smsmith /* 32069953Smsmith * If we've filled up the user's buffer, 32169953Smsmith * break out at this point. Since we've 32269953Smsmith * got a match here, we'll pick right back 32369953Smsmith * up at the matching entry. We can also 32469953Smsmith * tell the user that there are more matches 32569953Smsmith * left. 32669953Smsmith */ 32769953Smsmith if (cio->num_matches >= ionum) 32869953Smsmith break; 32969953Smsmith 330116702Sjmg /* only if can copy it out do we count it */ 331116702Sjmg if (!(error = copyout(&dinfo->conf, 332116702Sjmg &cio->matches[cio->num_matches], 333116702Sjmg sizeof(struct pci_conf)))) { 334116702Sjmg cio->num_matches++; 335116702Sjmg } 33669953Smsmith } 33769953Smsmith } 33869953Smsmith 33969953Smsmith /* 34069953Smsmith * Set the pointer into the list, so if the user is getting 34169953Smsmith * n records at a time, where n < pci_numdevs, 34269953Smsmith */ 34369953Smsmith cio->offset = i; 34469953Smsmith 34569953Smsmith /* 34669953Smsmith * Set the generation, the user will need this if they make 34769953Smsmith * another ioctl call with offset != 0. 34869953Smsmith */ 34969953Smsmith cio->generation = pci_generation; 35069953Smsmith 35169953Smsmith /* 35269953Smsmith * If this is the last device, inform the user so he won't 35369953Smsmith * bother asking for more devices. If dinfo isn't NULL, we 35469953Smsmith * know that there are more matches in the list because of 35569953Smsmith * the way the traversal is done. 35669953Smsmith */ 35769953Smsmith if (dinfo == NULL) 35869953Smsmith cio->status = PCI_GETCONF_LAST_DEVICE; 35969953Smsmith else 36069953Smsmith cio->status = PCI_GETCONF_MORE_DEVS; 36169953Smsmith 362116702Sjmggetconfexit: 36369953Smsmith if (pattern_buf != NULL) 36469953Smsmith free(pattern_buf, M_TEMP); 36569953Smsmith 36669953Smsmith break; 36769953Smsmith } 368121013Sse 36969953Smsmith case PCIOCREAD: 370121013Sse case PCIOCWRITE: 37169953Smsmith io = (struct pci_io *)data; 37269953Smsmith switch(io->pi_width) { 37369953Smsmith case 4: 37469953Smsmith case 2: 37569953Smsmith case 1: 376121013Sse /* make sure register is in bounds and aligned */ 377121013Sse if (cmd == PCIOCREAD || cmd == PCIOCWRITE) 378121013Sse if (io->pi_reg < 0 || 379121013Sse io->pi_reg + io->pi_width > PCI_REGMAX || 380121013Sse io->pi_reg & (io->pi_width - 1)) 381121013Sse error = EINVAL; 38269953Smsmith /* 38369953Smsmith * Assume that the user-level bus number is 384145022Sbms * in fact the physical PCI bus number. 385145022Sbms * Look up the grandparent, i.e. the bridge device, 386145022Sbms * so that we can issue configuration space cycles. 38769953Smsmith */ 388145022Sbms pcidev = pci_find_bsf(io->pi_sel.pc_bus, 389145022Sbms io->pi_sel.pc_dev, io->pi_sel.pc_func); 390145022Sbms if (pcidev) { 391145022Sbms device_t busdev, brdev; 392145022Sbms 393145022Sbms busdev = device_get_parent(pcidev); 394145022Sbms brdev = device_get_parent(busdev); 395145022Sbms 396121013Sse if (cmd == PCIOCWRITE) 397145022Sbms PCIB_WRITE_CONFIG(brdev, 398145022Sbms io->pi_sel.pc_bus, 399121013Sse io->pi_sel.pc_dev, 400121013Sse io->pi_sel.pc_func, 401121013Sse io->pi_reg, 402121013Sse io->pi_data, 403121013Sse io->pi_width); 404121013Sse else 405121013Sse io->pi_data = 406145022Sbms PCIB_READ_CONFIG(brdev, 407145022Sbms io->pi_sel.pc_bus, 408121013Sse io->pi_sel.pc_dev, 409121013Sse io->pi_sel.pc_func, 410121013Sse io->pi_reg, 411121013Sse io->pi_width); 41269953Smsmith error = 0; 41369953Smsmith } else { 41469953Smsmith error = ENODEV; 41569953Smsmith } 41669953Smsmith break; 41769953Smsmith default: 418116702Sjmg error = EINVAL; 41969953Smsmith break; 42069953Smsmith } 42169953Smsmith break; 42269953Smsmith 42369953Smsmith default: 42469953Smsmith error = ENOTTY; 42569953Smsmith break; 42669953Smsmith } 42769953Smsmith 42869953Smsmith return (error); 42969953Smsmith} 430