The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/kern_lkm.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $NetBSD: kern_lkm.c,v 1.98 2007/12/20 23:03:08 dsl Exp $        */
    2 
    3 /*
    4  * Copyright (c) 1994 Christopher G. Demetriou
    5  * Copyright (c) 1992 Terrence R. Lambert.
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  * 3. All advertising materials mentioning features or use of this software
   17  *    must display the following acknowledgement:
   18  *      This product includes software developed by Terrence R. Lambert.
   19  * 4. The name Terrence R. Lambert may not be used to endorse or promote
   20  *    products derived from this software without specific prior written
   21  *    permission.
   22  *
   23  * THIS SOFTWARE IS PROVIDED BY TERRENCE R. LAMBERT ``AS IS'' AND ANY
   24  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
   27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   33  * SUCH DAMAGE.
   34  */
   35 
   36 /*
   37  * XXX it's not really safe to unload *any* of the types which are
   38  * currently loadable; e.g. you could unload a syscall which was being
   39  * blocked in, etc.  In the long term, a solution should be come up
   40  * with, but "not right now." -- cgd
   41  */
   42 
   43 #include <sys/cdefs.h>
   44 __KERNEL_RCSID(0, "$NetBSD: kern_lkm.c,v 1.98 2007/12/20 23:03:08 dsl Exp $");
   45 
   46 #include "opt_ddb.h"
   47 #include "opt_malloclog.h"
   48 
   49 #include <sys/param.h>
   50 #include <sys/systm.h>
   51 #include <sys/ioctl.h>
   52 #include <sys/tty.h>
   53 #include <sys/file.h>
   54 #include <sys/proc.h>
   55 #include <sys/uio.h>
   56 #include <sys/kernel.h>
   57 #include <sys/vnode.h>
   58 #include <sys/malloc.h>
   59 #include <sys/mount.h>
   60 #include <sys/exec.h>
   61 #include <sys/syscallargs.h>
   62 #include <sys/conf.h>
   63 #include <sys/ksyms.h>
   64 #include <sys/device.h>
   65 #include <sys/once.h>
   66 #include <sys/kauth.h>
   67 
   68 #include <sys/lkm.h>
   69 #include <sys/syscall.h>
   70 #ifdef DDB
   71 #include <machine/db_machdep.h>
   72 #include <ddb/db_sym.h>
   73 #endif
   74 
   75 #include <uvm/uvm_extern.h>
   76 
   77 struct vm_map *lkm_map;
   78 
   79 #define LKM_SPACE_ALLOC(size, exec) \
   80         uvm_km_alloc(lkm_map, (size), 0, \
   81                 UVM_KMF_WIRED | ((exec) ? UVM_KMF_EXEC : 0))
   82 #define LKM_SPACE_FREE(addr, size) \
   83         uvm_km_free(lkm_map, (addr), (size), UVM_KMF_WIRED)
   84 
   85 #if !defined(DEBUG) && defined(LKMDEBUG)
   86 # define DEBUG
   87 #endif
   88 
   89 #ifdef DEBUG
   90 # define LKMDB_INFO     0x01
   91 # define LKMDB_LOAD     0x02
   92 int     lkmdebug = 0;
   93 #endif
   94 
   95 #define LKM_ALLOC       0x01
   96 
   97 #define LKMS_IDLE       0x00
   98 #define LKMS_RESERVED   0x01
   99 #define LKMS_LOADING    0x02
  100 #define LKMS_UNLOADING  0x08
  101 
  102 static int      lkm_v = 0;
  103 static int      lkm_state = LKMS_IDLE;
  104 
  105 static TAILQ_HEAD(lkms_head, lkm_table) lkmods = /* table of loaded modules */
  106                 TAILQ_HEAD_INITIALIZER(lkmods);
  107 static struct lkm_table *curp;                  /* global for in-progress ops */
  108 
  109 static struct lkm_table *lkmlookup(int, char *, int, int *);
  110 static struct lkm_table *lkmalloc(void);
  111 static void lkmfree(void);
  112 static void lkmunreserve(int);
  113 static int _lkm_syscall(struct lkm_table *, int);
  114 static int _lkm_vfs(struct lkm_table *, int);
  115 static int _lkm_dev(struct lkm_table *, int);
  116 #ifdef STREAMS
  117 static int _lkm_strmod(struct lkm_table *, int);
  118 #endif
  119 static int _lkm_exec(struct lkm_table *, int);
  120 static int _lkm_compat(struct lkm_table *, int);
  121 static int _lkm_drv(struct lkm_table *, int);
  122 
  123 static int _lkm_checkver(struct lkm_table *);
  124 
  125 dev_type_open(lkmopen);
  126 dev_type_close(lkmclose);
  127 dev_type_ioctl(lkmioctl);
  128 
  129 const struct cdevsw lkm_cdevsw = {
  130         lkmopen, lkmclose, noread, nowrite, lkmioctl,
  131         nostop, notty, nopoll, nommap, nokqfilter, D_OTHER,
  132 };
  133 
  134 static ONCE_DECL(lkm_init_once);
  135 
  136 static int
  137 lkm_init(void)
  138 {
  139         /*
  140          * If machine-dependent code hasn't initialized the lkm_map
  141          * then just use kernel_map.
  142          */
  143         if (lkm_map == NULL)
  144                 lkm_map = kernel_map;
  145 
  146         return 0;
  147 }
  148 
  149 /*ARGSUSED*/
  150 int
  151 lkmopen(dev_t dev, int flag, int devtype, struct lwp *l)
  152 {
  153         int error;
  154 
  155         RUN_ONCE(&lkm_init_once, lkm_init);
  156 
  157         if (minor(dev) != 0)
  158                 return (ENXIO);         /* bad minor # */
  159 
  160         /*
  161          * Use of the loadable kernel module device must be exclusive; we
  162          * may try to remove this restriction later, but it's really no
  163          * hardship.
  164          */
  165         while (lkm_v & LKM_ALLOC) {
  166                 if (flag & FNONBLOCK)           /* don't hang */
  167                         return (EBUSY);
  168                 /*
  169                  * Sleep pending unlock; we use tsleep() to allow
  170                  * an alarm out of the open.
  171                  */
  172                 error = tsleep((void *)&lkm_v, TTIPRI|PCATCH, "lkmopn", 0);
  173                 if (error)
  174                         return (error);
  175         }
  176         lkm_v |= LKM_ALLOC;
  177 
  178         return (0);             /* pseudo-device open */
  179 }
  180 
  181 /*
  182  * Look up for a LKM in the list.
  183  */
  184 static struct lkm_table *
  185 lkmlookup(int i, char *name, int need_copyin, int *error)
  186 {
  187         struct lkm_table *p;
  188         char istr[MAXLKMNAME];
  189 
  190         /*
  191          * p being NULL here implies the list is empty, so any lookup is
  192          * invalid (name based or otherwise). Since the list of modules is
  193          * kept sorted by id, lowest to highest, the id of the last entry
  194          * will be the highest in use.
  195          */
  196         p = TAILQ_LAST(&lkmods, lkms_head);
  197         if (p == NULL || i > p->id) {
  198                 *error = EINVAL;
  199                 return (NULL);
  200         }
  201 
  202         if (i < 0) {            /* unload by name */
  203                 /*
  204                  * Copy name and lookup id from all loaded
  205                  * modules.  May fail.
  206                  */
  207                 if (need_copyin) {
  208                         *error = copyinstr(name, istr, MAXLKMNAME - 1, NULL);
  209                         if (*error)
  210                                 return (NULL);
  211                 } else
  212                         strncpy(istr, name, MAXLKMNAME - 1);
  213                 istr[MAXLKMNAME - 1] = '\0';
  214 
  215                 TAILQ_FOREACH(p, &lkmods, link) {
  216                         if (strcmp(istr, p->private.lkm_any->lkm_name) == 0)
  217                                 break;
  218                 }
  219         } else
  220                 TAILQ_FOREACH(p, &lkmods, link)
  221                         if (i == p->id)
  222                                 break;
  223 
  224         if (p == NULL)
  225                 *error = ENOENT;
  226 
  227         return (p);
  228 }
  229 
  230 /*
  231  * Allocates memory for a new LKM table entry and inserts in the list.
  232  * Returns NULL on failure.
  233  */
  234 static struct lkm_table *
  235 lkmalloc(void)
  236 {
  237         struct lkm_table *p, *ret;
  238         int id = 0;
  239 
  240         ret = malloc(sizeof(struct lkm_table), M_DEVBUF, M_NOWAIT);
  241         if (ret == NULL)
  242                 return (NULL);
  243         ret->refcnt = 0;
  244         ret->forced = 0;
  245 
  246         /* find the first unused id */
  247         TAILQ_FOREACH(p, &lkmods, link) {
  248                 if (id != p->id)
  249                         break;
  250                 id++;
  251         }
  252         ret->id = id;
  253 
  254         if (p == NULL)
  255                 TAILQ_INSERT_TAIL(&lkmods, ret, link);
  256         else
  257                 TAILQ_INSERT_BEFORE(p, ret, link);
  258 
  259         return (ret);
  260 }
  261 
  262 /*
  263  * Frees the current LKM table entry.
  264  */
  265 static void
  266 lkmfree(void)
  267 {
  268         TAILQ_REMOVE(&lkmods, curp, link);
  269         free(curp, M_DEVBUF);
  270         curp = NULL;
  271 }
  272 
  273 /*
  274  * Unreserve the memory associated with the current loaded module; done on
  275  * a coerced close of the lkm device (close on premature exit of modload)
  276  * or explicitly by modload as a result of a link failure.
  277  */
  278 static void
  279 lkmunreserve(int delsymtab)
  280 {
  281 
  282         if (lkm_state == LKMS_IDLE)
  283                 return;
  284 
  285         if (curp && curp->syms) {
  286                 if (delsymtab)
  287                         ksyms_delsymtab(curp->private.lkm_any->lkm_name);
  288                 LKM_SPACE_FREE(curp->syms, curp->sym_size);
  289                 curp->syms = 0;
  290         }
  291         /*
  292          * Actually unreserve the memory
  293          */
  294         if (curp && curp->area) {
  295                 LKM_SPACE_FREE(curp->area, curp->size);
  296                 curp->area = 0;
  297         }
  298 
  299         if (curp && curp->forced)
  300                 curp->forced = 0;
  301 
  302         lkm_state = LKMS_IDLE;
  303 }
  304 
  305 int
  306 lkmclose(dev_t dev, int flag, int mode, struct lwp *l)
  307 {
  308 
  309         if (!(lkm_v & LKM_ALLOC)) {
  310 #ifdef DEBUG
  311                 if (lkmdebug & LKMDB_INFO)
  312                         printf("LKM: close before open!\n");
  313 #endif  /* DEBUG */
  314                 return (EBADF);
  315         }
  316 
  317         /* do this before waking the herd... */
  318         if (curp != NULL && curp->refcnt == 0) {
  319                 /*
  320                  * If we close before setting used, we have aborted
  321                  * by way of error or by way of close-on-exit from
  322                  * a premature exit of "modload".
  323                  */
  324                 lkmunreserve(1);        /* coerce state to LKM_IDLE */
  325                 lkmfree();
  326         }
  327 
  328         lkm_v &= ~LKM_ALLOC;
  329         wakeup((void *)&lkm_v); /* thundering herd "problem" here */
  330 
  331         return (0);             /* pseudo-device closed */
  332 }
  333 
  334 /*ARGSUSED*/
  335 int
  336 lkmioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
  337 {
  338         int i, error = 0;
  339         struct lmc_resrv *resrvp;
  340         struct lmc_loadbuf *loadbufp;
  341         struct lmc_unload *unloadp;
  342         struct lmc_stat  *statp;
  343 
  344         switch(cmd) {
  345         case LMRESERV:          /* reserve pages for a module */
  346                 if (kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_LKM,
  347                     0, (void *)cmd, NULL, NULL))
  348                         return EPERM;
  349 
  350                 if ((flag & FWRITE) == 0) /* only allow this if writing */
  351                         return EPERM;
  352 
  353                 resrvp = (struct lmc_resrv *)data;
  354 
  355                 curp = lkmalloc();
  356                 if (curp == NULL) {
  357                         error = ENOMEM;
  358                         break;
  359                 }
  360                 resrvp->slot = curp->id;        /* return slot */
  361 
  362                 /*
  363                  * Get memory for module
  364                  */
  365                 curp->size = resrvp->size;
  366                 curp->area = LKM_SPACE_ALLOC(curp->size, 1);
  367                 curp->offset = 0;               /* load offset */
  368 
  369                 resrvp->addr = curp->area;      /* ret kernel addr */
  370 
  371                 if (resrvp->sym_size) {
  372                         curp->sym_size = resrvp->sym_size;
  373                         curp->sym_symsize = resrvp->sym_symsize;
  374                         curp->syms = (u_long)LKM_SPACE_ALLOC(curp->sym_size, 0);
  375                         curp->sym_offset = 0;
  376                         resrvp->sym_addr = curp->syms; /* ret symbol addr */
  377                 } else {
  378                         curp->sym_size = 0;
  379                         curp->syms = 0;
  380                         curp->sym_offset = 0;
  381                         resrvp->sym_addr = 0;
  382                 }
  383 
  384 #ifdef DEBUG
  385                 if (lkmdebug & LKMDB_INFO) {
  386                         printf("LKM: LMRESERV (actual   = 0x%08lx)\n",
  387                             curp->area);
  388                         printf("LKM: LMRESERV (syms     = 0x%08lx)\n",
  389                                curp->syms);
  390                         printf("LKM: LMRESERV (adjusted = 0x%08lx)\n",
  391                             trunc_page(curp->area));
  392                 }
  393 #endif /* DEBUG */
  394                 lkm_state = LKMS_RESERVED;
  395                 break;
  396 
  397         case LMLOADBUF:         /* Copy in; stateful, follows LMRESERV */
  398                 if (kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_LKM,
  399                     0, (void *)cmd, NULL, NULL))
  400                         return EPERM;
  401 
  402                 if ((flag & FWRITE) == 0) /* only allow this if writing */
  403                         return EPERM;
  404 
  405                 loadbufp = (struct lmc_loadbuf *)data;
  406                 i = loadbufp->cnt;
  407                 if ((lkm_state != LKMS_RESERVED && lkm_state != LKMS_LOADING)
  408                     || i < 0
  409                     || i > MODIOBUF
  410                     || i > curp->size - curp->offset) {
  411                         error = ENOMEM;
  412                         break;
  413                 }
  414 
  415                 /* copy in buffer full of data */
  416                 error = copyin(loadbufp->data,
  417                                (char *)curp->area + curp->offset, i);
  418                 if (error)
  419                         break;
  420 
  421 #ifdef PMAP_NEED_PROCWR
  422                 pmap_procwr(&proc0, curp->area + curp->offset, i);
  423 #endif
  424                 if ((curp->offset + i) < curp->size) {
  425                         lkm_state = LKMS_LOADING;
  426 #ifdef DEBUG
  427                         if (lkmdebug & LKMDB_LOAD)
  428                 printf("LKM: LMLOADBUF (loading @ %ld of %ld, i = %d)\n",
  429                             curp->offset, curp->size, i);
  430 #endif /* DEBUG */
  431                 }
  432                 curp->offset += i;
  433                 break;
  434 
  435         case LMLOADSYMS:        /* Copy in; stateful, follows LMRESERV*/
  436                 if ((flag & FWRITE) == 0) /* only allow this if writing */
  437                         return EPERM;
  438 
  439                 loadbufp = (struct lmc_loadbuf *)data;
  440                 i = loadbufp->cnt;
  441                 if ((lkm_state != LKMS_LOADING)
  442                     || i < 0
  443                     || i > MODIOBUF
  444                     || i > curp->sym_size - curp->sym_offset) {
  445                         error = ENOMEM;
  446                         break;
  447                 }
  448 
  449                 /* copy in buffer full of data*/
  450                 if ((error = copyin(loadbufp->data,
  451                                    (char *)(curp->syms) + curp->sym_offset,
  452                                    i)) != 0)
  453                         break;
  454 
  455                 if ((curp->sym_offset + i) < curp->sym_size) {
  456                         lkm_state = LKMS_LOADING;
  457 #ifdef DEBUG
  458                         if (lkmdebug & LKMDB_LOAD)
  459                 printf( "LKM: LMLOADSYMS (loading @ %ld of %ld, i = %d)\n",
  460                         curp->sym_offset, curp->sym_size, i);
  461 #endif  /* DEBUG*/
  462                 }
  463                 curp->sym_offset += i;
  464                 break;
  465 
  466         case LMUNRESRV:         /* discard reserved pages for a module */
  467                 if (kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_LKM,
  468                     0, (void *)cmd, NULL, NULL))
  469                         return EPERM;
  470 
  471                 if ((flag & FWRITE) == 0) /* only allow this if writing */
  472                         return EPERM;
  473 
  474                 lkmunreserve(0);        /* coerce state to LKM_IDLE */
  475                 if (curp != NULL)
  476                         lkmfree();
  477 #ifdef DEBUG
  478                 if (lkmdebug & LKMDB_INFO)
  479                         printf("LKM: LMUNRESERV\n");
  480 #endif /* DEBUG */
  481                 break;
  482 
  483         case LMREADY:           /* module loaded: call entry */
  484                 if (kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_LKM,
  485                     0, (void *)cmd, NULL, NULL))
  486                         return EPERM;
  487 
  488                 if ((flag & FWRITE) == 0) /* only allow this if writing */
  489                         return EPERM;
  490 
  491                 if (lkm_state != LKMS_LOADING) {
  492 #ifdef DEBUG
  493                         if (lkmdebug & LKMDB_INFO)
  494                                 printf("lkm_state is %02x\n", lkm_state);
  495 #endif /* DEBUG */
  496                         return ENXIO;
  497                 }
  498 
  499                 if (curp->size - curp->offset > 0) {
  500                         /* The remainder must be bss, so we clear it */
  501                         memset((char *)curp->area + curp->offset, 0,
  502                                curp->size - curp->offset);
  503                 }
  504 
  505 #ifdef DDB
  506                 /*
  507                  * Temporarily load the symbol table before the entry
  508                  * routine is called, so that the symbols are available
  509                  * for DDB backtrace and breakpoints.
  510                  */
  511                 if (curp->syms && curp->sym_offset >= curp->sym_size) {
  512                         error = ksyms_addsymtab("/lkmtemp/",
  513                             (char *)curp->syms, curp->sym_symsize,
  514                             (char *)curp->syms + curp->sym_symsize,
  515                             curp->sym_size - curp->sym_symsize);
  516 
  517                         if (error)
  518                                 goto rdyfail;
  519 
  520 #ifdef DEBUG
  521                         if (lkmdebug & LKMDB_INFO)
  522                                 printf( "DDB symbols added!\n" );
  523 #endif
  524                 }
  525 #endif /* DDB */
  526 
  527                 curp->entry = (int (*)(struct lkm_table *, int, int))
  528                                 (*((long *) (data)));
  529 
  530                 /* call entry(load)... (assigns "private" portion) */
  531                 error = (*(curp->entry))(curp, LKM_E_LOAD, LKM_VERSION);
  532 
  533                 if (curp->syms && curp->sym_offset >= curp->sym_size) {
  534 #ifdef DDB
  535                         ksyms_delsymtab("/lkmtemp/");
  536 #endif
  537 
  538                         if (!error) {
  539                                 error = ksyms_addsymtab(curp->private.lkm_any->lkm_name,
  540                                     (char *)curp->syms, curp->sym_symsize,
  541                                     (char *)curp->syms + curp->sym_symsize,
  542                                     curp->sym_size - curp->sym_symsize);
  543                         }
  544                 }
  545 
  546                 if (error) {
  547 #ifdef DDB
  548     rdyfail:
  549 #endif
  550                         /*
  551                          * Module may refuse loading or may have a
  552                          * version mismatch...
  553                          */
  554                         lkm_state = LKMS_UNLOADING;     /* for lkmunreserve */
  555                         lkmunreserve(0);                /* free memory */
  556                         lkmfree();                      /* free slot */
  557 #ifdef DEBUG
  558                         if (lkmdebug & LKMDB_INFO)
  559                                 printf("lkm entry point failed with error %d\n",
  560                                    error);
  561 #endif /* DEBUG */
  562                         break;
  563                 }
  564                 curp->refcnt++;
  565 
  566 #ifdef DEBUG
  567                 if (lkmdebug & LKMDB_INFO)
  568                         printf("LKM: LMREADY\n");
  569 #endif /* DEBUG */
  570                 lkm_state = LKMS_IDLE;
  571                 break;
  572 
  573         case LMUNLOAD:          /* unload a module */
  574                 if (kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_LKM,
  575                     0, (void *)cmd, NULL, NULL))
  576                         return EPERM;
  577 
  578                 if ((flag & FWRITE) == 0) /* only allow this if writing */
  579                         return EPERM;
  580 
  581                 unloadp = (struct lmc_unload *)data;
  582 
  583                 curp = lkmlookup(unloadp->id, unloadp->name, 1, &error);
  584                 if (curp == NULL)
  585                         break;
  586 
  587                 /* call entry(unload) */
  588                 if ((*(curp->entry))(curp, LKM_E_UNLOAD, LKM_VERSION)) {
  589                         error = EBUSY;
  590                         break;
  591                 }
  592 
  593                 lkm_state = LKMS_UNLOADING;     /* non-idle for lkmunreserve */
  594                 lkmunreserve(1);                /* free memory */
  595                 lkmfree();                      /* free slot */
  596                 break;
  597 
  598         case LMSTAT:            /* stat a module by id/name */
  599                 /* allow readers and writers to stat */
  600 
  601                 statp = (struct lmc_stat *)data;
  602 
  603                 if ((curp = lkmlookup(statp->id, statp->name, 0, &error)) == NULL)
  604                         break;
  605 
  606                 if ((error = (*curp->entry)(curp, LKM_E_STAT, LKM_VERSION)))
  607                         break;
  608 
  609                 /*
  610                  * Copy out stat information for this module...
  611                  */
  612                 statp->id       = curp->id;
  613                 statp->offset   = curp->private.lkm_any->lkm_offset;
  614                 statp->type     = curp->private.lkm_any->lkm_type;
  615                 statp->area     = curp->area;
  616                 statp->size     = curp->size / 1024;
  617                 statp->private  = (unsigned long)curp->private.lkm_any;
  618                 statp->ver      = LKM_VERSION;
  619                 copystr(curp->private.lkm_any->lkm_name,
  620                           statp->name,
  621                           MAXLKMNAME - 2,
  622                           (size_t *)0);
  623 
  624                 break;
  625 
  626 #ifdef LMFORCE
  627         case LMFORCE:           /* stateful, optionally follows LMRESERV */
  628                 if (kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_LKM,
  629                     0, (void *)cmd, NULL, NULL))
  630                         return EPERM;
  631 
  632                 if ((flag & FWRITE) == 0) /* only allow this if writing */
  633                         return EPERM;
  634 
  635                 if (lkm_state != LKMS_RESERVED) {
  636                         error = EPERM;
  637                         break;
  638                 }
  639 
  640                 curp->forced = (*(u_long *)data != 0);
  641                 break;
  642 #endif /* LMFORCE */
  643 
  644         default:                /* bad ioctl()... */
  645                 error = ENOTTY;
  646                 break;
  647         }
  648 
  649         return (error);
  650 }
  651 
  652 /*
  653  * Acts like "nosys" but can be identified in sysent for dynamic call
  654  * number assignment for a limited number of calls.
  655  *
  656  * Place holder for system call slots reserved for loadable modules.
  657  */
  658 int
  659 sys_lkmnosys(struct lwp *l, const void *v, register_t *retval)
  660 {
  661 
  662         return (sys_nosys(l, v, retval));
  663 }
  664 
  665 /*
  666  * A placeholder function for load/unload/stat calls; simply returns zero.
  667  * Used where people don't want to specify a special function.
  668  */
  669 int
  670 lkm_nofunc(struct lkm_table *lkmtp, int cmd)
  671 {
  672 
  673         return (0);
  674 }
  675 
  676 int
  677 lkmexists(struct lkm_table *lkmtp)
  678 {
  679         struct lkm_table *p;
  680 
  681         /* see if name exists... */
  682         TAILQ_FOREACH(p, &lkmods, link) {
  683                 if (strcmp(lkmtp->private.lkm_any->lkm_name,
  684                     p->private.lkm_any->lkm_name) == 0 && (p->refcnt != 0))
  685                         return (1);             /* already loaded... */
  686         }
  687 
  688         return (0);             /* module not loaded... */
  689 }
  690 
  691 /*
  692  * For the loadable system call described by the structure pointed to
  693  * by lkmtp, load/unload/stat it depending on the cmd requested.
  694  */
  695 static int
  696 _lkm_syscall(struct lkm_table *lkmtp, int cmd)
  697 {
  698         struct lkm_syscall *args = lkmtp->private.lkm_syscall;
  699         int i;
  700         int error = 0;
  701 
  702         switch(cmd) {
  703         case LKM_E_LOAD:
  704                 /* don't load twice! */
  705                 if (lkmexists(lkmtp))
  706                         return (EEXIST);
  707 
  708                 if ((i = args->mod.lkm_offset) == -1) { /* auto */
  709                         /*
  710                          * Search the table looking for a slot...
  711                          */
  712                         for (i = 0; i < SYS_MAXSYSCALL; i++)
  713                                 if (sysent[i].sy_call == sys_lkmnosys)
  714                                         break;          /* found it! */
  715                         /* out of allocable slots? */
  716                         if (i == SYS_MAXSYSCALL) {
  717                                 error = ENFILE;
  718                                 break;
  719                         }
  720                 } else {                                /* assign */
  721                         if (i < 0 || i >= SYS_MAXSYSCALL) {
  722                                 error = EINVAL;
  723                                 break;
  724                         }
  725                 }
  726 
  727                 /* save old */
  728                 memcpy(&args->lkm_oldent, &sysent[i], sizeof(struct sysent));
  729 
  730                 /* replace with new */
  731                 memcpy(&sysent[i], args->lkm_sysent, sizeof(struct sysent));
  732 
  733                 /* done! */
  734                 args->mod.lkm_offset = i;       /* slot in sysent[] */
  735 
  736                 break;
  737 
  738         case LKM_E_UNLOAD:
  739                 /* current slot... */
  740                 i = args->mod.lkm_offset;
  741 
  742                 /* replace current slot contents with old contents */
  743                 memcpy(&sysent[i], &args->lkm_oldent, sizeof(struct sysent));
  744 
  745                 break;
  746 
  747         case LKM_E_STAT:        /* no special handling... */
  748                 break;
  749         }
  750 
  751         return (error);
  752 }
  753 
  754 /*
  755  * For the loadable virtual file system described by the structure pointed
  756  * to by lkmtp, load/unload/stat it depending on the cmd requested.
  757  */
  758 static int
  759 _lkm_vfs(struct lkm_table *lkmtp, int cmd)
  760 {
  761         struct lkm_vfs *args = lkmtp->private.lkm_vfs;
  762         int error = 0;
  763 
  764         switch(cmd) {
  765         case LKM_E_LOAD:
  766                 /* don't load twice! */
  767                 if (lkmexists(lkmtp))
  768                         return (EEXIST);
  769 
  770                 /* Establish the file system. */
  771                 if ((error = vfs_attach(args->lkm_vfsops)) != 0)
  772                         return (error);
  773 
  774                 /* done! */
  775                 break;
  776 
  777         case LKM_E_UNLOAD:
  778                 /* Disestablish the file system. */
  779                 if ((error = vfs_detach(args->lkm_vfsops)) != 0)
  780                         return (error);
  781                 break;
  782 
  783         case LKM_E_STAT:        /* no special handling... */
  784                 break;
  785         }
  786 
  787         return (error);
  788 }
  789 
  790 /*
  791  * For the loadable device driver described by the structure pointed to
  792  * by lkmtp, load/unload/stat it depending on the cmd requested.
  793  */
  794 static int
  795 _lkm_dev(struct lkm_table *lkmtp, int cmd)
  796 {
  797         struct lkm_dev *args = lkmtp->private.lkm_dev;
  798         int error;
  799 
  800         switch(cmd) {
  801         case LKM_E_LOAD:
  802                 /* don't load twice! */
  803                 if (lkmexists(lkmtp))
  804                         return (EEXIST);
  805 
  806                 error = devsw_attach(args->lkm_devname,
  807                                      args->lkm_bdev, &args->lkm_bdevmaj,
  808                                      args->lkm_cdev, &args->lkm_cdevmaj);
  809                 if (error != 0)
  810                         return (error);
  811 
  812                 args->mod.lkm_offset =
  813                         LKM_MAKEMAJOR(args->lkm_bdevmaj, args->lkm_cdevmaj);
  814                 break;
  815 
  816         case LKM_E_UNLOAD:
  817                 devsw_detach(args->lkm_bdev, args->lkm_cdev);
  818                 args->lkm_bdevmaj = -1;
  819                 args->lkm_cdevmaj = -1;
  820                 break;
  821 
  822         case LKM_E_STAT:        /* no special handling... */
  823                 break;
  824         }
  825 
  826         return (0);
  827 }
  828 
  829 #ifdef STREAMS
  830 /*
  831  * For the loadable streams module described by the structure pointed to
  832  * by lkmtp, load/unload/stat it depending on the cmd requested.
  833  */
  834 static int
  835 _lkm_strmod(struct lkm_table *lkmtp, int cmd)
  836 {
  837         struct lkm_strmod *args = lkmtp->private.lkm_strmod;
  838         int i;
  839         int error = 0;
  840 
  841         switch(cmd) {
  842         case LKM_E_LOAD:
  843                 /* don't load twice! */
  844                 if (lkmexists(lkmtp))
  845                         return (EEXIST);
  846                 break;
  847 
  848         case LKM_E_UNLOAD:
  849                 break;
  850 
  851         case LKM_E_STAT:        /* no special handling... */
  852                 break;
  853         }
  854 
  855         return (error);
  856 }
  857 #endif  /* STREAMS */
  858 
  859 /*
  860  * For the loadable execution class described by the structure pointed to
  861  * by lkmtp, load/unload/stat it depending on the cmd requested.
  862  */
  863 static int
  864 _lkm_exec(struct lkm_table *lkmtp, int cmd)
  865 {
  866         struct lkm_exec *args = lkmtp->private.lkm_exec;
  867         int error = 0;
  868 
  869         switch(cmd) {
  870         case LKM_E_LOAD:
  871                 /* don't load twice! */
  872                 if (lkmexists(lkmtp))
  873                         return (EEXIST);
  874 
  875                 /* this would also fill in the emulation pointer in
  876                  * args->lkm_execsw */
  877                 error = exec_add(args->lkm_execsw, args->lkm_emul);
  878                 break;
  879 
  880         case LKM_E_UNLOAD:
  881                 error = exec_remove(args->lkm_execsw);
  882                 break;
  883 
  884         case LKM_E_STAT:        /* no special handling... */
  885                 break;
  886         }
  887 
  888         return (error);
  889 }
  890 
  891 /*
  892  * For the loadable compat/emulation class described by the structure pointed to
  893  * by lkmtp, load/unload/stat it depending on the cmd requested.
  894  */
  895 static int
  896 _lkm_compat(struct lkm_table *lkmtp, int cmd)
  897 {
  898         struct lkm_compat *args = lkmtp->private.lkm_compat;
  899         int error = 0;
  900 
  901         switch(cmd) {
  902         case LKM_E_LOAD:
  903                 /* don't load twice! */
  904                 if (lkmexists(lkmtp))
  905                         return (EEXIST);
  906 
  907                 error = emul_register(args->lkm_compat, 0);
  908                 break;
  909 
  910         case LKM_E_UNLOAD:
  911                 error = emul_unregister(args->lkm_compat->e_name);
  912                 break;
  913 
  914         case LKM_E_STAT:        /* no special handling... */
  915                 break;
  916         }
  917 
  918         return (error);
  919 }
  920 
  921 static int
  922 drvlkm_load(struct cfdriver **cd, const struct cfattachlkminit *cai,
  923             struct cfdata *cf)
  924 {
  925         const struct cfattachlkminit *cfai;
  926         int i, error, j;
  927 
  928         for (i = 0; cd[i]; i++) {
  929                 error = config_cfdriver_attach(cd[i]);
  930                 if (!error)
  931                         continue;
  932                 if (error != EEXIST) {
  933                         printf("%s: unable to register driver\n",
  934                                cd[i]->cd_name);
  935                         /* XXX roll back previous attachments */
  936                         goto out;
  937                 }
  938                 printf("driver %s already present\n", cd[i]->cd_name);
  939                 /*
  940                  * get existing drivers out of the list so we won't try
  941                  * to detach them
  942                  */
  943                 for (j = i; cd[j]; j++)
  944                         cd[j] = cd[j + 1];
  945                 i--; /* continue at same index */
  946         }
  947 
  948         for (cfai = cai; cfai->cfai_name; cfai++) {
  949                 for (i = 0; cfai->cfai_list[i]; i++) {
  950                         error = config_cfattach_attach(cfai->cfai_name,
  951                                                        cfai->cfai_list[i]);
  952                         if (!error)
  953                                 continue;
  954                         if (error != EEXIST) {
  955                                 printf("%s: unable to register cfattach\n",
  956                                        cfai->cfai_list[i]->ca_name);
  957                                 /* XXX roll back previous attachments */
  958                                 goto out;
  959                         }
  960                         printf("driver attachment %s for %s already present\n",
  961                                cfai->cfai_list[i]->ca_name, cfai->cfai_name);
  962                         /*
  963                          * get existing attachments out of the list so we
  964                          * won't try to detach them
  965                          */
  966                         for (j = i; cfai->cfai_list[j]; j++)
  967                                 cfai->cfai_list[j] = cfai->cfai_list[j + 1];
  968                         i--; /* continue at same index */
  969                 }
  970         }
  971 
  972         error = config_cfdata_attach(cf, 1);
  973         /* XXX roll back cfdriver / cfattach attachments in error case */
  974 
  975 out:
  976         return (error);
  977 }
  978 
  979 static int
  980 drvlkm_unload(struct cfdriver **cd, const struct cfattachlkminit *cai,
  981               struct cfdata *cf)
  982 {
  983         const struct cfattachlkminit *cfai;
  984         int i, error;
  985 
  986         error = config_cfdata_detach(cf);
  987         if (error)
  988                 return (error);
  989 
  990         for (cfai = cai; cfai->cfai_name; cfai++) {
  991                 for (i = 0; cfai->cfai_list[i]; i++) {
  992                         error = config_cfattach_detach(cfai->cfai_name,
  993                                                        cfai->cfai_list[i]);
  994                         if (error) {
  995                                 printf("%s: unable to deregister cfattach\n",
  996                                        cfai->cfai_list[i]->ca_name);
  997                                 return (error);
  998                         }
  999                 }
 1000         }
 1001 
 1002         for (i = 0; cd[i]; i++) {
 1003                 error = config_cfdriver_detach(cd[i]);
 1004                 if (error) {
 1005                         printf("%s: unable to deregister cfdriver\n",
 1006                                 cd[i]->cd_name);
 1007                         return (error);
 1008                 }
 1009         }
 1010 
 1011         return (0);
 1012 }
 1013 
 1014 static int
 1015 _lkm_drv(struct lkm_table *lkmtp, int cmd)
 1016 {
 1017         struct lkm_drv *args = lkmtp->private.lkm_drv;
 1018         int error = 0;
 1019 
 1020         switch(cmd) {
 1021         case LKM_E_LOAD:
 1022                 /* don't load twice! */
 1023                 if (lkmexists(lkmtp))
 1024                         return (EEXIST);
 1025 
 1026                 error = drvlkm_load(args->lkm_cd,
 1027                                     args->lkm_cai,
 1028                                     args->lkm_cf);
 1029                 break;
 1030 
 1031         case LKM_E_UNLOAD:
 1032                 error = drvlkm_unload(args->lkm_cd,
 1033                                       args->lkm_cai,
 1034                                       args->lkm_cf);
 1035                 break;
 1036 
 1037         case LKM_E_STAT:        /* no special handling... */
 1038                 break;
 1039         }
 1040 
 1041         return (error);
 1042 }
 1043 
 1044 /*
 1045  * This code handles the per-module type "wiring-in" of loadable modules
 1046  * into existing kernel tables.  For "LM_MISC" modules, wiring and unwiring
 1047  * is assumed to be done in their entry routines internal to the module
 1048  * itself.
 1049  */
 1050 int
 1051 lkmdispatch(struct lkm_table *lkmtp, int cmd)
 1052 {
 1053         int error = 0;          /* default = success */
 1054 #ifdef DEBUG
 1055         if (lkmdebug & LKMDB_INFO)
 1056                 printf( "lkmdispatch: %p %d\n", lkmtp, cmd );
 1057 #endif
 1058 
 1059         /* If loading, check the LKM is compatible */
 1060         if (cmd == LKM_E_LOAD) {
 1061                 if (_lkm_checkver(lkmtp))
 1062                         return (EPROGMISMATCH);
 1063         }
 1064 
 1065         switch(lkmtp->private.lkm_any->lkm_type) {
 1066         case LM_SYSCALL:
 1067                 error = _lkm_syscall(lkmtp, cmd);
 1068                 break;
 1069 
 1070         case LM_VFS:
 1071                 error = _lkm_vfs(lkmtp, cmd);
 1072                 break;
 1073 
 1074         case LM_DEV:
 1075                 error = _lkm_dev(lkmtp, cmd);
 1076                 break;
 1077 
 1078 #ifdef STREAMS
 1079         case LM_STRMOD:
 1080             {
 1081                 struct lkm_strmod *args = lkmtp->private.lkm_strmod;
 1082             }
 1083                 break;
 1084 
 1085 #endif  /* STREAMS */
 1086 
 1087         case LM_EXEC:
 1088                 error = _lkm_exec(lkmtp, cmd);
 1089                 break;
 1090 
 1091         case LM_COMPAT:
 1092                 error = _lkm_compat(lkmtp, cmd);
 1093                 break;
 1094 
 1095         case LM_MISC:   /* ignore content -- no "misc-specific" procedure */
 1096                 break;
 1097 
 1098         case LM_DRV:
 1099                 error = _lkm_drv(lkmtp, cmd);
 1100                 break;
 1101 
 1102         default:
 1103                 error = ENXIO;  /* unknown type */
 1104                 break;
 1105         }
 1106 
 1107         return (error);
 1108 }
 1109 
 1110 /*
 1111  * Check LKM version against current kernel.
 1112  */
 1113 static int
 1114 _lkm_checkver(struct lkm_table *lkmtp)
 1115 {
 1116         struct lkm_any *mod = lkmtp->private.lkm_any;
 1117 
 1118         if (mod->lkm_modver != LKM_VERSION) {
 1119                 printf("LKM '%s': LKM version mismatch - LKM %d, kernel %d\n",
 1120                     mod->lkm_name, mod->lkm_modver, LKM_VERSION);
 1121                 return (1);
 1122         }
 1123 
 1124         if (lkmtp->forced) {
 1125                 printf("LKM '%s': forced load, skipping compatibility checks\n",
 1126                     mod->lkm_name);
 1127                 return (0);
 1128         }
 1129 
 1130         if (mod->lkm_sysver != __NetBSD_Version__) {
 1131                 printf("LKM '%s': kernel version mismatch - LKM %d, kernel %d\n",
 1132                     mod->lkm_name, mod->lkm_sysver, __NetBSD_Version__);
 1133                 return (2);
 1134         }
 1135 
 1136         /*
 1137          * Following might eventually be changed to take into account envdep,
 1138          * if it's non-NULL.
 1139          */
 1140         if (strcmp(mod->lkm_envver, _LKM_ENV_VERSION) != 0) {
 1141                 const char *kenv = _LKM_ENV_VERSION;
 1142                 const char *envver = mod->lkm_envver;
 1143 
 1144                 if (kenv[0] == ',')
 1145                         kenv++;
 1146                 if (envver[0] == ',')
 1147                         envver++;
 1148 
 1149                 printf("LKM '%s': environment compile options mismatch - LKM '%s', kernel '%s'\n",
 1150                     mod->lkm_name, envver, kenv);
 1151                 return (3);
 1152         }
 1153 
 1154         /*
 1155          * Basic parameters match, LKM is hopefully compatible.
 1156          * Cross fingers and approve.
 1157          */
 1158         return (0);
 1159 }

Cache object: 2190d533852ed3d9c5a4200cc4882395


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.