pci_user.c revision 244695
159290Sjlemon/*- 272969Sjlemon * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 3133741Sjmg * All rights reserved. 4197240Ssson * 559290Sjlemon * Redistribution and use in source and binary forms, with or without 659290Sjlemon * modification, are permitted provided that the following conditions 759290Sjlemon * are met: 859290Sjlemon * 1. Redistributions of source code must retain the above copyright 959290Sjlemon * notice unmodified, this list of conditions, and the following 1059290Sjlemon * disclaimer. 1159290Sjlemon * 2. Redistributions in binary form must reproduce the above copyright 1259290Sjlemon * notice, this list of conditions and the following disclaimer in the 1359290Sjlemon * documentation and/or other materials provided with the distribution. 1459290Sjlemon * 1559290Sjlemon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1659290Sjlemon * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1759290Sjlemon * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1859290Sjlemon * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1959290Sjlemon * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2059290Sjlemon * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2159290Sjlemon * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2259290Sjlemon * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2359290Sjlemon * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2459290Sjlemon * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2559290Sjlemon */ 2659290Sjlemon 2759290Sjlemon#include <sys/cdefs.h> 2859290Sjlemon__FBSDID("$FreeBSD: head/sys/dev/pci/pci_user.c 244695 2012-12-26 13:07:17Z davidxu $"); 29116182Sobrien 30116182Sobrien#include "opt_bus.h" /* XXX trim includes */ 31116182Sobrien#include "opt_compat.h" 32162592Sjmg 33162592Sjmg#include <sys/param.h> 3459290Sjlemon#include <sys/systm.h> 3559290Sjlemon#include <sys/malloc.h> 3659290Sjlemon#include <sys/module.h> 3776166Smarkm#include <sys/linker.h> 3876166Smarkm#include <sys/fcntl.h> 3959290Sjlemon#include <sys/conf.h> 40132138Salfred#include <sys/kernel.h> 4159290Sjlemon#include <sys/proc.h> 4259290Sjlemon#include <sys/queue.h> 43108524Salfred#include <sys/types.h> 44132138Salfred 4559290Sjlemon#include <vm/vm.h> 46133741Sjmg#include <vm/pmap.h> 4770834Swollman#include <vm/vm_extern.h> 4859290Sjlemon 4959290Sjlemon#include <sys/bus.h> 5059290Sjlemon#include <machine/bus.h> 5159290Sjlemon#include <sys/rman.h> 5259290Sjlemon#include <machine/resource.h> 53132138Salfred 54132138Salfred#include <sys/pciio.h> 5559290Sjlemon#include <dev/pci/pcireg.h> 5659290Sjlemon#include <dev/pci/pcivar.h> 5759290Sjlemon 5884138Sjlemon#include "pcib_if.h" 5959290Sjlemon#include "pci_if.h" 60142934Sps 61133741Sjmg/* 6259290Sjlemon * This is the user interface to PCI configuration space. 63162592Sjmg */ 64162592Sjmg 65162592Sjmgstatic d_open_t pci_open; 6659290Sjlemonstatic d_close_t pci_close; 6792751Sjeffstatic int pci_conf_match(struct pci_match_conf *matches, int num_matches, 6859290Sjlemon struct pci_conf *match_buf); 69141616Sphkstatic d_ioctl_t pci_ioctl; 70141616Sphk 71133741Sjmgstruct cdevsw pcicdev = { 72133741Sjmg .d_version = D_VERSION, 73133741Sjmg .d_flags = D_NEEDGIANT, 74133741Sjmg .d_open = pci_open, 75133741Sjmg .d_close = pci_close, 76133741Sjmg .d_ioctl = pci_ioctl, 77133741Sjmg .d_name = "pci", 78133741Sjmg}; 79133741Sjmg 80133741Sjmgstatic int 81133741Sjmgpci_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 82133741Sjmg{ 83133741Sjmg int error; 84133741Sjmg 85133741Sjmg if (oflags & FWRITE) { 86133741Sjmg error = securelevel_gt(td->td_ucred, 0); 8784138Sjlemon if (error) 88133741Sjmg return (error); 89133741Sjmg } 90146950Sps 91146950Sps return (0); 92162594Sjmg} 93162594Sjmg 94170029Srwatsonstatic int 95133741Sjmgpci_close(struct cdev *dev, int flag, int devtype, struct thread *td) 96133741Sjmg{ 97133741Sjmg return 0; 98133741Sjmg} 99133741Sjmg 100146950Sps/* 101146950Sps * Match a single pci_conf structure against an array of pci_match_conf 102146950Sps * structures. The first argument, 'matches', is an array of num_matches 10359290Sjlemon * pci_match_conf structures. match_buf is a pointer to the pci_conf 104133741Sjmg * structure that will be compared to every entry in the matches array. 105133741Sjmg * This function returns 1 on failure, 0 on success. 10659290Sjlemon */ 107108255Sphkstatic int 108108255Sphkpci_conf_match(struct pci_match_conf *matches, int num_matches, 109175140Sjhb struct pci_conf *match_buf) 110108255Sphk{ 111108255Sphk int i; 112108255Sphk 113108255Sphk if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) 114108255Sphk return(1); 115108238Sphk 11672521Sjlemon for (i = 0; i < num_matches; i++) { 117116546Sphk /* 118116546Sphk * I'm not sure why someone would do this...but... 119175140Sjhb */ 120116546Sphk if (matches[i].flags == PCI_GETCONF_NO_MATCH) 121116546Sphk continue; 122116546Sphk 123116546Sphk /* 124116546Sphk * Look at each of the match flags. If it's set, do the 12572521Sjlemon * comparison. If the comparison fails, we don't have a 12672521Sjlemon * match, go on to the next item if there is one. 127133741Sjmg */ 12883366Sjulian if (((matches[i].flags & PCI_GETCONF_MATCH_DOMAIN) != 0) 12959290Sjlemon && (match_buf->pc_sel.pc_domain != 13059290Sjlemon matches[i].pc_sel.pc_domain)) 13159290Sjlemon continue; 132133741Sjmg 13359290Sjlemon if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) 13459290Sjlemon && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) 13572521Sjlemon continue; 13672521Sjlemon 13772521Sjlemon if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) 13872521Sjlemon && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) 13972521Sjlemon continue; 14072521Sjlemon 14179989Sjlemon if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) 14279989Sjlemon && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) 14379989Sjlemon continue; 14479989Sjlemon 145197241Ssson if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) 146197241Ssson && (match_buf->pc_vendor != matches[i].pc_vendor)) 147197241Ssson continue; 148197241Ssson 14972521Sjlemon if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) 150197134Srwatson && (match_buf->pc_device != matches[i].pc_device)) 151197134Srwatson continue; 152197134Srwatson 153197134Srwatson if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) 154197134Srwatson && (match_buf->pc_class != matches[i].pc_class)) 155197134Srwatson continue; 156197134Srwatson 157197134Srwatson if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) 158197134Srwatson && (match_buf->pd_unit != matches[i].pd_unit)) 159133741Sjmg continue; 160197134Srwatson 161197134Srwatson if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) 162197134Srwatson && (strncmp(matches[i].pd_name, match_buf->pd_name, 163197134Srwatson sizeof(match_buf->pd_name)) != 0)) 164197134Srwatson continue; 165197134Srwatson 166197134Srwatson return(0); 167197134Srwatson } 168197134Srwatson 169197134Srwatson return(1); 170197134Srwatson} 171197134Srwatson 172197241Ssson#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ 173197241Ssson defined(COMPAT_FREEBSD6) 174197241Ssson#define PRE7_COMPAT 175197241Ssson 176197241Sssontypedef enum { 177197241Ssson PCI_GETCONF_NO_MATCH_OLD = 0x00, 17872521Sjlemon PCI_GETCONF_MATCH_BUS_OLD = 0x01, 17992751Sjeff PCI_GETCONF_MATCH_DEV_OLD = 0x02, 18084138Sjlemon PCI_GETCONF_MATCH_FUNC_OLD = 0x04, 18184138Sjlemon PCI_GETCONF_MATCH_NAME_OLD = 0x08, 18284138Sjlemon PCI_GETCONF_MATCH_UNIT_OLD = 0x10, 18384138Sjlemon PCI_GETCONF_MATCH_VENDOR_OLD = 0x20, 18459290Sjlemon PCI_GETCONF_MATCH_DEVICE_OLD = 0x40, 185133741Sjmg PCI_GETCONF_MATCH_CLASS_OLD = 0x80 186133741Sjmg} pci_getconf_flags_old; 187133741Sjmg 188133741Sjmgstruct pcisel_old { 189133741Sjmg u_int8_t pc_bus; /* bus number */ 190133741Sjmg u_int8_t pc_dev; /* device on this bus */ 191133741Sjmg u_int8_t pc_func; /* function on this device */ 192133741Sjmg}; 193133741Sjmg 194133741Sjmgstruct pci_conf_old { 195133741Sjmg struct pcisel_old pc_sel; /* bus+slot+function */ 19659290Sjlemon u_int8_t pc_hdr; /* PCI header type */ 197133741Sjmg u_int16_t pc_subvendor; /* card vendor ID */ 198133741Sjmg u_int16_t pc_subdevice; /* card device ID, assigned by 199133741Sjmg card vendor */ 200133741Sjmg u_int16_t pc_vendor; /* chip vendor ID */ 201133741Sjmg u_int16_t pc_device; /* chip device ID, assigned by 202133741Sjmg chip vendor */ 203133741Sjmg u_int8_t pc_class; /* chip PCI class */ 204133741Sjmg u_int8_t pc_subclass; /* chip PCI subclass */ 205133741Sjmg u_int8_t pc_progif; /* chip PCI programming interface */ 206133741Sjmg u_int8_t pc_revid; /* chip revision ID */ 207133741Sjmg char pd_name[PCI_MAXNAMELEN + 1]; /* device name */ 208133741Sjmg u_long pd_unit; /* device unit number */ 209133741Sjmg}; 210133741Sjmg 211133741Sjmgstruct pci_match_conf_old { 212133741Sjmg struct pcisel_old pc_sel; /* bus+slot+function */ 213133741Sjmg char pd_name[PCI_MAXNAMELEN + 1]; /* device name */ 214133741Sjmg u_long pd_unit; /* Unit number */ 215133741Sjmg u_int16_t pc_vendor; /* PCI Vendor ID */ 216133741Sjmg u_int16_t pc_device; /* PCI Device ID */ 217133741Sjmg u_int8_t pc_class; /* PCI class */ 218133741Sjmg pci_getconf_flags_old flags; /* Matching expression */ 219133741Sjmg}; 220133741Sjmg 221147730Sssouhlalstruct pci_io_old { 222133741Sjmg struct pcisel_old pi_sel; /* device to operate on */ 223133741Sjmg int pi_reg; /* configuration register to examine */ 224147730Sssouhlal int pi_width; /* width (in bytes) of read or write */ 225147730Sssouhlal u_int32_t pi_data; /* data to write or result of read */ 226133741Sjmg}; 227147730Sssouhlal 228147730Sssouhlal#ifdef COMPAT_FREEBSD32 229147730Sssouhlalstruct pci_conf_old32 { 230147730Sssouhlal struct pcisel_old pc_sel; /* bus+slot+function */ 231147730Sssouhlal uint8_t pc_hdr; /* PCI header type */ 232147730Sssouhlal uint16_t pc_subvendor; /* card vendor ID */ 233147730Sssouhlal uint16_t pc_subdevice; /* card device ID, assigned by 234147730Sssouhlal card vendor */ 235193951Skib uint16_t pc_vendor; /* chip vendor ID */ 236147730Sssouhlal uint16_t pc_device; /* chip device ID, assigned by 237193951Skib chip vendor */ 238193951Skib uint8_t pc_class; /* chip PCI class */ 239147730Sssouhlal uint8_t pc_subclass; /* chip PCI subclass */ 240147730Sssouhlal uint8_t pc_progif; /* chip PCI programming interface */ 241147730Sssouhlal uint8_t pc_revid; /* chip revision ID */ 242147730Sssouhlal char pd_name[PCI_MAXNAMELEN + 1]; /* device name */ 243147730Sssouhlal uint32_t pd_unit; /* device unit number (u_long) */ 24459290Sjlemon}; 24559290Sjlemon 24659290Sjlemonstruct pci_match_conf_old32 { 24759290Sjlemon struct pcisel_old pc_sel; /* bus+slot+function */ 24888633Salfred char pd_name[PCI_MAXNAMELEN + 1]; /* device name */ 24988633Salfred uint32_t pd_unit; /* Unit number (u_long) */ 25088633Salfred uint16_t pc_vendor; /* PCI Vendor ID */ 25188633Salfred uint16_t pc_device; /* PCI Device ID */ 25288633Salfred uint8_t pc_class; /* PCI class */ 25388633Salfred pci_getconf_flags_old flags; /* Matching expression */ 25488633Salfred}; 255197134Srwatson 256197134Srwatsonstruct pci_conf_io32 { 257197134Srwatson uint32_t pat_buf_len; /* pattern buffer length */ 258197134Srwatson uint32_t num_patterns; /* number of patterns */ 25988633Salfred uint32_t patterns; /* pattern buffer 260133741Sjmg (struct pci_match_conf_old32 *) */ 26159290Sjlemon uint32_t match_buf_len; /* match buffer length */ 262131562Salfred uint32_t num_matches; /* number of matches returned */ 26359290Sjlemon uint32_t matches; /* match buffer 26459290Sjlemon (struct pci_conf_old32 *) */ 26572521Sjlemon uint32_t offset; /* offset into device list */ 26659290Sjlemon uint32_t generation; /* device list generation */ 267133741Sjmg pci_getconf_status status; /* request status */ 268133741Sjmg}; 269133741Sjmg 270133741Sjmg#define PCIOCGETCONF_OLD32 _IOWR('p', 1, struct pci_conf_io32) 271133741Sjmg#endif /* COMPAT_FREEBSD32 */ 272133741Sjmg 273133741Sjmg#define PCIOCGETCONF_OLD _IOWR('p', 1, struct pci_conf_io) 274133741Sjmg#define PCIOCREAD_OLD _IOWR('p', 2, struct pci_io_old) 275133741Sjmg#define PCIOCWRITE_OLD _IOWR('p', 3, struct pci_io_old) 276133741Sjmg 277133741Sjmgstatic int pci_conf_match_old(struct pci_match_conf_old *matches, 278133741Sjmg int num_matches, struct pci_conf *match_buf); 279133741Sjmg 280133741Sjmgstatic int 281133741Sjmgpci_conf_match_old(struct pci_match_conf_old *matches, int num_matches, 282133741Sjmg struct pci_conf *match_buf) 283151260Sambrisko{ 284197241Ssson int i; 28559290Sjlemon 28659290Sjlemon if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) 287133741Sjmg return(1); 288133741Sjmg 289133741Sjmg for (i = 0; i < num_matches; i++) { 290133741Sjmg if (match_buf->pc_sel.pc_domain != 0) 29159290Sjlemon continue; 29272521Sjlemon 29359290Sjlemon /* 294133635Sjmg * I'm not sure why someone would do this...but... 29572521Sjlemon */ 29659290Sjlemon if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD) 29759290Sjlemon continue; 29872521Sjlemon 29959290Sjlemon /* 30072521Sjlemon * Look at each of the match flags. If it's set, do the 30159290Sjlemon * comparison. If the comparison fails, we don't have a 302109153Sdillon * match, go on to the next item if there is one. 30359290Sjlemon */ 30472521Sjlemon if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0) 305133741Sjmg && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) 30659290Sjlemon continue; 307133741Sjmg 30872521Sjlemon if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0) 309133741Sjmg && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) 310133741Sjmg continue; 31159290Sjlemon 31259290Sjlemon if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0) 31359290Sjlemon && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) 31459290Sjlemon continue; 31559290Sjlemon 31659290Sjlemon if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0) 317109153Sdillon && (match_buf->pc_vendor != matches[i].pc_vendor)) 31859290Sjlemon continue; 319133741Sjmg 32059290Sjlemon if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0) 32159290Sjlemon && (match_buf->pc_device != matches[i].pc_device)) 32259290Sjlemon continue; 32359290Sjlemon 32459290Sjlemon if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0) 32559290Sjlemon && (match_buf->pc_class != matches[i].pc_class)) 326109153Sdillon continue; 32759290Sjlemon 32859290Sjlemon if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0) 32959290Sjlemon && (match_buf->pd_unit != matches[i].pd_unit)) 33059290Sjlemon continue; 33159290Sjlemon 332133741Sjmg if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0) 33359290Sjlemon && (strncmp(matches[i].pd_name, match_buf->pd_name, 33459290Sjlemon sizeof(match_buf->pd_name)) != 0)) 33559290Sjlemon continue; 33659290Sjlemon 337113377Skbyanc return(0); 33875451Srwatson } 33959290Sjlemon 340113377Skbyanc return(1); 34159290Sjlemon} 342113377Skbyanc 343113377Skbyanc#ifdef COMPAT_FREEBSD32 344113377Skbyancstatic int 345133741Sjmgpci_conf_match_old32(struct pci_match_conf_old32 *matches, int num_matches, 346133741Sjmg struct pci_conf *match_buf) 347113377Skbyanc{ 348133741Sjmg int i; 349122019Scognet 350122019Scognet if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) 351133741Sjmg return(1); 35275451Srwatson 35359290Sjlemon for (i = 0; i < num_matches; i++) { 35459290Sjlemon if (match_buf->pc_sel.pc_domain != 0) 35559290Sjlemon continue; 35659290Sjlemon 35759290Sjlemon /* 35859290Sjlemon * I'm not sure why someone would do this...but... 35959290Sjlemon */ 36059290Sjlemon if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD) 36159290Sjlemon continue; 36259290Sjlemon 36359290Sjlemon /* 36459290Sjlemon * Look at each of the match flags. If it's set, do the 36559290Sjlemon * comparison. If the comparison fails, we don't have a 366122686Scognet * match, go on to the next item if there is one. 367133741Sjmg */ 368113377Skbyanc if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0) && 369113377Skbyanc (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) 370113377Skbyanc continue; 371113377Skbyanc 372113377Skbyanc if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0) && 373113377Skbyanc (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) 374113377Skbyanc continue; 375133741Sjmg 376113377Skbyanc if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0) && 37771500Sjhb (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) 37859290Sjlemon continue; 37959290Sjlemon 38059290Sjlemon if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0) && 38159290Sjlemon (match_buf->pc_vendor != matches[i].pc_vendor)) 38259290Sjlemon continue; 38359290Sjlemon 38459290Sjlemon if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0) && 38559290Sjlemon (match_buf->pc_device != matches[i].pc_device)) 38659290Sjlemon continue; 38759290Sjlemon 38859290Sjlemon if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0) && 38959290Sjlemon (match_buf->pc_class != matches[i].pc_class)) 390133741Sjmg continue; 39159290Sjlemon 39259290Sjlemon if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0) && 39359290Sjlemon ((u_int32_t)match_buf->pd_unit != matches[i].pd_unit)) 394133741Sjmg continue; 39559290Sjlemon 396133741Sjmg if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0) && 397133741Sjmg (strncmp(matches[i].pd_name, match_buf->pd_name, 398133741Sjmg sizeof(match_buf->pd_name)) != 0)) 39959290Sjlemon continue; 40059290Sjlemon 401133741Sjmg return (0); 40259290Sjlemon } 40359290Sjlemon 40459290Sjlemon return (1); 405133741Sjmg} 40659290Sjlemon#endif /* COMPAT_FREEBSD32 */ 40759290Sjlemon#endif /* PRE7_COMPAT */ 40859290Sjlemon 40959290Sjlemonstatic int 41059290Sjlemonpci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 41159290Sjlemon{ 41259290Sjlemon device_t pcidev, brdev; 41359290Sjlemon void *confdata; 41459290Sjlemon const char *name; 41559290Sjlemon struct devlist *devlist_head; 41659290Sjlemon struct pci_conf_io *cio = NULL; 41759290Sjlemon struct pci_devinfo *dinfo; 41859290Sjlemon struct pci_io *io; 41959290Sjlemon struct pci_bar_io *bio; 42059290Sjlemon struct pci_match_conf *pattern_buf; 42159290Sjlemon struct pci_map *pm; 42259290Sjlemon size_t confsz, iolen, pbufsz; 423133741Sjmg int error, ionum, i, num_patterns; 424133741Sjmg#ifdef PRE7_COMPAT 425133590Srwatson#ifdef COMPAT_FREEBSD32 426164451Sjhb struct pci_conf_io32 *cio32 = NULL; 427133741Sjmg struct pci_conf_old32 conf_old32; 42859290Sjlemon struct pci_match_conf_old32 *pattern_buf_old32 = NULL; 42959290Sjlemon#endif 43059290Sjlemon struct pci_conf_old conf_old; 431180340Skib struct pci_io iodata; 432180340Skib struct pci_io_old *io_old; 43359290Sjlemon struct pci_match_conf_old *pattern_buf_old = NULL; 434180340Skib 435180340Skib io_old = NULL; 436180340Skib 437180340Skib if (!(flag & FWRITE) && cmd != PCIOCGETBAR && 438180340Skib cmd != PCIOCGETCONF && cmd != PCIOCGETCONF_OLD) 439180340Skib return EPERM; 440180340Skib#else 441180340Skib if (!(flag & FWRITE) && cmd != PCIOCGETBAR && cmd != PCIOCGETCONF) 442180340Skib return EPERM; 443180340Skib#endif 444180340Skib 445180340Skib switch(cmd) { 446180340Skib#ifdef PRE7_COMPAT 447180340Skib#ifdef COMPAT_FREEBSD32 448180340Skib case PCIOCGETCONF_OLD32: 449180340Skib cio32 = (struct pci_conf_io32 *)data; 450180340Skib cio = malloc(sizeof(struct pci_conf_io), M_TEMP, M_WAITOK); 451180340Skib cio->pat_buf_len = cio32->pat_buf_len; 452180340Skib cio->num_patterns = cio32->num_patterns; 453180340Skib cio->patterns = (void *)(uintptr_t)cio32->patterns; 454180340Skib cio->match_buf_len = cio32->match_buf_len; 455180340Skib cio->num_matches = cio32->num_matches; 456180340Skib cio->matches = (void *)(uintptr_t)cio32->matches; 457180340Skib cio->offset = cio32->offset; 458180340Skib cio->generation = cio32->generation; 459180340Skib cio->status = cio32->status; 460180340Skib cio32->num_matches = 0; 461180340Skib break; 462180340Skib#endif 463180340Skib case PCIOCGETCONF_OLD: 46459290Sjlemon#endif 465180340Skib case PCIOCGETCONF: 46659290Sjlemon cio = (struct pci_conf_io *)data; 467180340Skib } 468180340Skib 469180340Skib switch(cmd) { 470180340Skib#ifdef PRE7_COMPAT 471180340Skib#ifdef COMPAT_FREEBSD32 472180340Skib case PCIOCGETCONF_OLD32: 473180340Skib#endif 474180340Skib case PCIOCGETCONF_OLD: 475180340Skib#endif 476180340Skib case PCIOCGETCONF: 477180340Skib 478180340Skib pattern_buf = NULL; 479180340Skib num_patterns = 0; 480180340Skib dinfo = NULL; 481180340Skib 482180340Skib cio->num_matches = 0; 483180340Skib 484180340Skib /* 485180340Skib * If the user specified an offset into the device list, 486180340Skib * but the list has changed since they last called this 487180340Skib * ioctl, tell them that the list has changed. They will 488180340Skib * have to get the list from the beginning. 489180340Skib */ 490180340Skib if ((cio->offset != 0) 49159290Sjlemon && (cio->generation != pci_generation)){ 49259290Sjlemon cio->status = PCI_GETCONF_LIST_CHANGED; 49359290Sjlemon error = 0; 494180340Skib goto getconfexit; 495180340Skib } 496180340Skib 497180340Skib /* 498180340Skib * Check to see whether the user has asked for an offset 49959290Sjlemon * past the end of our list. 50059290Sjlemon */ 501180340Skib if (cio->offset >= pci_numdevs) { 502180340Skib cio->status = PCI_GETCONF_LAST_DEVICE; 503180340Skib error = 0; 504180340Skib goto getconfexit; 50559290Sjlemon } 506180340Skib 50759290Sjlemon /* get the head of the device queue */ 50859290Sjlemon devlist_head = &pci_devq; 509133741Sjmg 510133741Sjmg /* 511133741Sjmg * Determine how much room we have for pci_conf structures. 512133741Sjmg * Round the user's buffer size down to the nearest 513133741Sjmg * multiple of sizeof(struct pci_conf) in case the user 514133741Sjmg * didn't specify a multiple of that size. 515133741Sjmg */ 516133741Sjmg#ifdef PRE7_COMPAT 517133741Sjmg#ifdef COMPAT_FREEBSD32 518133741Sjmg if (cmd == PCIOCGETCONF_OLD32) 519133741Sjmg confsz = sizeof(struct pci_conf_old32); 520133741Sjmg else 521133741Sjmg#endif 522133741Sjmg if (cmd == PCIOCGETCONF_OLD) 52379989Sjlemon confsz = sizeof(struct pci_conf_old); 52479989Sjlemon else 52579989Sjlemon#endif 52679989Sjlemon confsz = sizeof(struct pci_conf); 52784138Sjlemon iolen = min(cio->match_buf_len - (cio->match_buf_len % confsz), 52879989Sjlemon pci_numdevs * confsz); 52979989Sjlemon 530133741Sjmg /* 53179989Sjlemon * Since we know that iolen is a multiple of the size of 532133741Sjmg * the pciconf union, it's okay to do this. 53384138Sjlemon */ 534177860Sjeff ionum = iolen / confsz; 535133741Sjmg 53679989Sjlemon /* 53779989Sjlemon * If this test is true, the user wants the pci_conf 53879989Sjlemon * structures returned to match the supplied entries. 53979989Sjlemon */ 54079989Sjlemon if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs) 541133590Srwatson && (cio->pat_buf_len > 0)) { 542133741Sjmg /* 54379989Sjlemon * pat_buf_len needs to be: 54479989Sjlemon * num_patterns * sizeof(struct pci_match_conf) 54579989Sjlemon * While it is certainly possible the user just 54684138Sjlemon * allocated a large buffer, but set the number of 54779989Sjlemon * matches correctly, it is far more likely that 548133741Sjmg * their kernel doesn't match the userland utility 549133741Sjmg * they're using. It's also possible that the user 550133741Sjmg * forgot to initialize some variables. Yes, this 551133741Sjmg * may be overly picky, but I hazard to guess that 55284138Sjlemon * it's far more likely to just catch folks that 553133741Sjmg * updated their kernel but not their userland. 55484138Sjlemon */ 55579989Sjlemon#ifdef PRE7_COMPAT 556136500Sjmg#ifdef COMPAT_FREEBSD32 557184214Sdes if (cmd == PCIOCGETCONF_OLD32) 558142217Srwatson pbufsz = sizeof(struct pci_match_conf_old32); 559127982Scperciva else 560177860Sjeff#endif 561177860Sjeff if (cmd == PCIOCGETCONF_OLD) 56279989Sjlemon pbufsz = sizeof(struct pci_match_conf_old); 56379989Sjlemon else 56479989Sjlemon#endif 56579989Sjlemon pbufsz = sizeof(struct pci_match_conf); 566133741Sjmg if (cio->num_patterns * pbufsz != cio->pat_buf_len) { 56779989Sjlemon /* The user made a mistake, return an error. */ 56879989Sjlemon cio->status = PCI_GETCONF_ERROR; 56979989Sjlemon error = EINVAL; 57084138Sjlemon goto getconfexit; 57179989Sjlemon } 57284138Sjlemon 573127982Scperciva /* 574184205Sdes * Allocate a buffer to hold the patterns. 575133741Sjmg */ 576136500Sjmg#ifdef PRE7_COMPAT 57779989Sjlemon#ifdef COMPAT_FREEBSD32 57879989Sjlemon if (cmd == PCIOCGETCONF_OLD32) { 579133741Sjmg pattern_buf_old32 = malloc(cio->pat_buf_len, 58079989Sjlemon M_TEMP, M_WAITOK); 58179989Sjlemon error = copyin(cio->patterns, 58279989Sjlemon pattern_buf_old32, cio->pat_buf_len); 58379989Sjlemon } else 58479989Sjlemon#endif /* COMPAT_FREEBSD32 */ 58579989Sjlemon if (cmd == PCIOCGETCONF_OLD) { 58679989Sjlemon pattern_buf_old = malloc(cio->pat_buf_len, 587197241Ssson M_TEMP, M_WAITOK); 588197241Ssson error = copyin(cio->patterns, 589197241Ssson pattern_buf_old, cio->pat_buf_len); 590197241Ssson } else 591197241Ssson#endif /* PRE7_COMPAT */ 592197241Ssson { 593197241Ssson pattern_buf = malloc(cio->pat_buf_len, M_TEMP, 594197241Ssson M_WAITOK); 595197241Ssson error = copyin(cio->patterns, pattern_buf, 596197241Ssson cio->pat_buf_len); 597197241Ssson } 598197241Ssson if (error != 0) { 599197241Ssson error = EINVAL; 600197241Ssson goto getconfexit; 601197241Ssson } 602197241Ssson num_patterns = cio->num_patterns; 603197241Ssson } else if ((cio->num_patterns > 0) 604197241Ssson || (cio->pat_buf_len > 0)) { 605197241Ssson /* 606197241Ssson * The user made a mistake, spit out an error. 607197241Ssson */ 608197241Ssson cio->status = PCI_GETCONF_ERROR; 609197241Ssson error = EINVAL; 610197241Ssson goto getconfexit; 611197241Ssson } 612197241Ssson 613197241Ssson /* 614197241Ssson * Go through the list of devices and copy out the devices 615197241Ssson * that match the user's criteria. 616197241Ssson */ 617197241Ssson for (cio->num_matches = 0, error = 0, i = 0, 618197241Ssson dinfo = STAILQ_FIRST(devlist_head); 619197241Ssson (dinfo != NULL) && (cio->num_matches < ionum) 620197241Ssson && (error == 0) && (i < pci_numdevs) && (dinfo != NULL); 621197241Ssson dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { 622197241Ssson 623197241Ssson if (i < cio->offset) 624197241Ssson continue; 625197241Ssson 626197241Ssson /* Populate pd_name and pd_unit */ 627197241Ssson name = NULL; 628197241Ssson if (dinfo->cfg.dev) 629197241Ssson name = device_get_name(dinfo->cfg.dev); 630197241Ssson if (name) { 631197241Ssson strncpy(dinfo->conf.pd_name, name, 632197241Ssson sizeof(dinfo->conf.pd_name)); 633197241Ssson dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0; 634197241Ssson dinfo->conf.pd_unit = 635197241Ssson device_get_unit(dinfo->cfg.dev); 636197241Ssson } else { 637197241Ssson dinfo->conf.pd_name[0] = '\0'; 638197241Ssson dinfo->conf.pd_unit = 0; 639197241Ssson } 640197241Ssson 641197241Ssson#ifdef PRE7_COMPAT 642197241Ssson if ( 643197241Ssson#ifdef COMPAT_FREEBSD32 644197241Ssson (cmd == PCIOCGETCONF_OLD32 && 645197241Ssson (pattern_buf_old32 == NULL || 646197241Ssson pci_conf_match_old32(pattern_buf_old32, 647197241Ssson num_patterns, &dinfo->conf) == 0)) || 648197241Ssson#endif 649197241Ssson (cmd == PCIOCGETCONF_OLD && 650197241Ssson (pattern_buf_old == NULL || 651197241Ssson pci_conf_match_old(pattern_buf_old, num_patterns, 652197241Ssson &dinfo->conf) == 0)) || 653197241Ssson (cmd == PCIOCGETCONF && 654197241Ssson (pattern_buf == NULL || 655197241Ssson pci_conf_match(pattern_buf, num_patterns, 656197241Ssson &dinfo->conf) == 0))) { 657197241Ssson#else 658197241Ssson if (pattern_buf == NULL || 659197241Ssson pci_conf_match(pattern_buf, num_patterns, 660197241Ssson &dinfo->conf) == 0) { 661197241Ssson#endif 662197241Ssson /* 663197241Ssson * If we've filled up the user's buffer, 664197241Ssson * break out at this point. Since we've 665197241Ssson * got a match here, we'll pick right back 666197241Ssson * up at the matching entry. We can also 667197241Ssson * tell the user that there are more matches 668197241Ssson * left. 669197241Ssson */ 670197241Ssson if (cio->num_matches >= ionum) 671197241Ssson break; 672197241Ssson 673197241Ssson#ifdef PRE7_COMPAT 674197241Ssson#ifdef COMPAT_FREEBSD32 67561468Sjlemon if (cmd == PCIOCGETCONF_OLD32) { 67683366Sjulian conf_old32.pc_sel.pc_bus = 67759290Sjlemon dinfo->conf.pc_sel.pc_bus; 67882710Sdillon conf_old32.pc_sel.pc_dev = 67959290Sjlemon dinfo->conf.pc_sel.pc_dev; 68061468Sjlemon conf_old32.pc_sel.pc_func = 68161468Sjlemon dinfo->conf.pc_sel.pc_func; 68259290Sjlemon conf_old32.pc_hdr = dinfo->conf.pc_hdr; 68383366Sjulian conf_old32.pc_subvendor = 68483366Sjulian dinfo->conf.pc_subvendor; 68561468Sjlemon conf_old32.pc_subdevice = 68682710Sdillon dinfo->conf.pc_subdevice; 687133741Sjmg conf_old32.pc_vendor = 688121256Sdwmalone dinfo->conf.pc_vendor; 689133741Sjmg conf_old32.pc_device = 690133741Sjmg dinfo->conf.pc_device; 69189306Salfred conf_old32.pc_class = 692133741Sjmg dinfo->conf.pc_class; 693193951Skib conf_old32.pc_subclass = 694133741Sjmg dinfo->conf.pc_subclass; 695133741Sjmg conf_old32.pc_progif = 696168355Srwatson dinfo->conf.pc_progif; 697133741Sjmg conf_old32.pc_revid = 698168355Srwatson dinfo->conf.pc_revid; 699133741Sjmg strncpy(conf_old32.pd_name, 700174988Sjeff dinfo->conf.pd_name, 701121256Sdwmalone sizeof(conf_old32.pd_name)); 702133741Sjmg conf_old32.pd_name[PCI_MAXNAMELEN] = 0; 70383366Sjulian conf_old32.pd_unit = 70482710Sdillon (uint32_t)dinfo->conf.pd_unit; 70561468Sjlemon confdata = &conf_old32; 70659290Sjlemon } else 70759290Sjlemon#endif /* COMPAT_FREEBSD32 */ 70859290Sjlemon if (cmd == PCIOCGETCONF_OLD) { 70959290Sjlemon conf_old.pc_sel.pc_bus = 71059290Sjlemon dinfo->conf.pc_sel.pc_bus; 71163977Speter conf_old.pc_sel.pc_dev = 71259290Sjlemon dinfo->conf.pc_sel.pc_dev; 71363452Sjlemon conf_old.pc_sel.pc_func = 71459290Sjlemon dinfo->conf.pc_sel.pc_func; 71563977Speter conf_old.pc_hdr = dinfo->conf.pc_hdr; 71659290Sjlemon conf_old.pc_subvendor = 71759290Sjlemon dinfo->conf.pc_subvendor; 71859290Sjlemon conf_old.pc_subdevice = 71983366Sjulian dinfo->conf.pc_subdevice; 72059290Sjlemon conf_old.pc_vendor = 721142934Sps dinfo->conf.pc_vendor; 722146950Sps conf_old.pc_device = 723146950Sps dinfo->conf.pc_device; 724146950Sps conf_old.pc_class = 725142934Sps dinfo->conf.pc_class; 726162592Sjmg conf_old.pc_subclass = 727162592Sjmg dinfo->conf.pc_subclass; 728162592Sjmg conf_old.pc_progif = 729162592Sjmg dinfo->conf.pc_progif; 730162592Sjmg conf_old.pc_revid = 731162592Sjmg dinfo->conf.pc_revid; 732142934Sps strncpy(conf_old.pd_name, 733142934Sps dinfo->conf.pd_name, 734142934Sps sizeof(conf_old.pd_name)); 735142934Sps conf_old.pd_name[PCI_MAXNAMELEN] = 0; 736142934Sps conf_old.pd_unit = 737142934Sps dinfo->conf.pd_unit; 738142934Sps confdata = &conf_old; 739142934Sps } else 740142934Sps#endif /* PRE7_COMPAT */ 741162592Sjmg confdata = &dinfo->conf; 742162592Sjmg /* Only if we can copy it out do we count it. */ 743162592Sjmg if (!(error = copyout(confdata, 744162592Sjmg (caddr_t)cio->matches + 745162592Sjmg confsz * cio->num_matches, confsz))) 746162592Sjmg cio->num_matches++; 747162592Sjmg } 748162592Sjmg } 749162592Sjmg 750162592Sjmg /* 751162592Sjmg * Set the pointer into the list, so if the user is getting 752162592Sjmg * n records at a time, where n < pci_numdevs, 753162592Sjmg */ 754162592Sjmg cio->offset = i; 755162592Sjmg 756162592Sjmg /* 757162592Sjmg * Set the generation, the user will need this if they make 758162592Sjmg * another ioctl call with offset != 0. 759162592Sjmg */ 760162592Sjmg cio->generation = pci_generation; 761162592Sjmg 762162592Sjmg /* 763162592Sjmg * If this is the last device, inform the user so he won't 764162592Sjmg * bother asking for more devices. If dinfo isn't NULL, we 765162592Sjmg * know that there are more matches in the list because of 766162592Sjmg * the way the traversal is done. 767162592Sjmg */ 768142934Sps if (dinfo == NULL) 769142934Sps cio->status = PCI_GETCONF_LAST_DEVICE; 770142934Sps else 771146950Sps cio->status = PCI_GETCONF_MORE_DEVS; 772142934Sps 773142934Spsgetconfexit: 774146950Sps#ifdef PRE7_COMPAT 775142934Sps#ifdef COMPAT_FREEBSD32 776146950Sps if (cmd == PCIOCGETCONF_OLD32) { 777142934Sps cio32->status = cio->status; 778142934Sps cio32->generation = cio->generation; 779146950Sps cio32->offset = cio->offset; 780146950Sps cio32->num_matches = cio->num_matches; 781146950Sps free(cio, M_TEMP); 782146950Sps } 783146950Sps if (pattern_buf_old32 != NULL) 784146950Sps free(pattern_buf_old32, M_TEMP); 785142934Sps#endif 786142934Sps if (pattern_buf_old != NULL) 787142934Sps free(pattern_buf_old, M_TEMP); 788146950Sps#endif 789146950Sps if (pattern_buf != NULL) 790146950Sps free(pattern_buf, M_TEMP); 791146950Sps 792146950Sps break; 793146950Sps 794146950Sps#ifdef PRE7_COMPAT 795146950Sps case PCIOCREAD_OLD: 796146950Sps case PCIOCWRITE_OLD: 797146950Sps io_old = (struct pci_io_old *)data; 798146950Sps iodata.pi_sel.pc_domain = 0; 799146950Sps iodata.pi_sel.pc_bus = io_old->pi_sel.pc_bus; 800146950Sps iodata.pi_sel.pc_dev = io_old->pi_sel.pc_dev; 801146950Sps iodata.pi_sel.pc_func = io_old->pi_sel.pc_func; 802146950Sps iodata.pi_reg = io_old->pi_reg; 803146950Sps iodata.pi_width = io_old->pi_width; 804146950Sps iodata.pi_data = io_old->pi_data; 805146950Sps data = (caddr_t)&iodata; 806142934Sps /* FALLTHROUGH */ 807146950Sps#endif 808146950Sps case PCIOCREAD: 809142934Sps case PCIOCWRITE: 810133741Sjmg io = (struct pci_io *)data; 811142934Sps switch(io->pi_width) { 81259290Sjlemon case 4: 81386341Sdillon case 2: 81459290Sjlemon case 1: 81559290Sjlemon /* Make sure register is not negative and aligned. */ 816142934Sps if (io->pi_reg < 0 || 81789319Salfred io->pi_reg & (io->pi_width - 1)) { 818170029Srwatson error = EINVAL; 819133741Sjmg break; 820133741Sjmg } 82159290Sjlemon /* 82259290Sjlemon * Assume that the user-level bus number is 823142934Sps * in fact the physical PCI bus number. 824146950Sps * Look up the grandparent, i.e. the bridge device, 825146950Sps * so that we can issue configuration space cycles. 826146950Sps */ 827146950Sps pcidev = pci_find_dbsf(io->pi_sel.pc_domain, 828146950Sps io->pi_sel.pc_bus, io->pi_sel.pc_dev, 82959290Sjlemon io->pi_sel.pc_func); 830142934Sps if (pcidev) { 831151260Sambrisko brdev = device_get_parent( 832151260Sambrisko device_get_parent(pcidev)); 83363452Sjlemon 834133741Sjmg#ifdef PRE7_COMPAT 835197243Ssson if (cmd == PCIOCWRITE || cmd == PCIOCWRITE_OLD) 836142934Sps#else 83763452Sjlemon if (cmd == PCIOCWRITE) 83863452Sjlemon#endif 839146950Sps PCIB_WRITE_CONFIG(brdev, 840146950Sps io->pi_sel.pc_bus, 841142934Sps io->pi_sel.pc_dev, 84259290Sjlemon io->pi_sel.pc_func, 84359290Sjlemon io->pi_reg, 84468883Sdillon io->pi_data, 84559290Sjlemon io->pi_width); 84659290Sjlemon#ifdef PRE7_COMPAT 84759290Sjlemon else if (cmd == PCIOCREAD_OLD) 848142934Sps io_old->pi_data = 84959290Sjlemon PCIB_READ_CONFIG(brdev, 85059290Sjlemon io->pi_sel.pc_bus, 851133741Sjmg io->pi_sel.pc_dev, 85268883Sdillon io->pi_sel.pc_func, 85368883Sdillon io->pi_reg, 85459290Sjlemon io->pi_width); 85559290Sjlemon#endif 856146950Sps else 85768883Sdillon io->pi_data = 858133741Sjmg PCIB_READ_CONFIG(brdev, 859133741Sjmg io->pi_sel.pc_bus, 860170066Srwatson io->pi_sel.pc_dev, 86159290Sjlemon io->pi_sel.pc_func, 86259290Sjlemon io->pi_reg, 86359290Sjlemon io->pi_width); 86459290Sjlemon error = 0; 86588633Salfred } else { 86688633Salfred#ifdef COMPAT_FREEBSD4 867133741Sjmg if (cmd == PCIOCREAD_OLD) { 86888633Salfred io_old->pi_data = -1; 869133741Sjmg error = 0; 870133741Sjmg } else 871133741Sjmg#endif 872133741Sjmg error = ENODEV; 873133741Sjmg } 874133741Sjmg break; 875133741Sjmg default: 876133741Sjmg error = EINVAL; 877133741Sjmg break; 878133741Sjmg } 879133741Sjmg break; 880133741Sjmg 881133741Sjmg case PCIOCGETBAR: 882133741Sjmg bio = (struct pci_bar_io *)data; 883133741Sjmg 884133741Sjmg /* 88588633Salfred * Assume that the user-level bus number is 88688633Salfred * in fact the physical PCI bus number. 88788633Salfred */ 88888633Salfred pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain, 88988633Salfred bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev, 89088633Salfred bio->pbi_sel.pc_func); 891133741Sjmg if (pcidev == NULL) { 89288633Salfred error = ENODEV; 893133741Sjmg break; 894133741Sjmg } 895133741Sjmg pm = pci_find_bar(pcidev, bio->pbi_reg); 896133741Sjmg if (pm == NULL) { 897133741Sjmg error = EINVAL; 898133741Sjmg break; 899133741Sjmg } 900133741Sjmg bio->pbi_base = pm->pm_value; 901133741Sjmg bio->pbi_length = (pci_addr_t)1 << pm->pm_size; 902133741Sjmg bio->pbi_enabled = pci_bar_enabled(pcidev, pm); 903133741Sjmg error = 0; 904133741Sjmg break; 905133741Sjmg case PCIOCATTACHED: 906133741Sjmg error = 0; 907133741Sjmg io = (struct pci_io *)data; 908133741Sjmg pcidev = pci_find_dbsf(io->pi_sel.pc_domain, io->pi_sel.pc_bus, 909133741Sjmg io->pi_sel.pc_dev, io->pi_sel.pc_func); 91088633Salfred if (pcidev != NULL) 91188633Salfred io->pi_data = device_is_attached(pcidev); 912133741Sjmg else 913133741Sjmg error = ENODEV; 914133741Sjmg break; 915133741Sjmg default: 916133741Sjmg error = ENOTTY; 917133741Sjmg break; 918133741Sjmg } 919133741Sjmg 920133741Sjmg return (error); 921133741Sjmg} 922133741Sjmg