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-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

    1 /*      $NetBSD: kern_lkm.c,v 1.72 2004/03/23 13:22:32 junyoung 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.72 2004/03/23 13:22:32 junyoung 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/sa.h>
   62 #include <sys/syscallargs.h>
   63 #include <sys/conf.h>
   64 #include <sys/ksyms.h>
   65 
   66 #include <sys/lkm.h>
   67 #include <sys/syscall.h>
   68 #ifdef DDB
   69 #include <machine/db_machdep.h>
   70 #include <ddb/db_sym.h>
   71 #endif
   72 
   73 #include <uvm/uvm_extern.h>
   74 
   75 struct vm_map *lkm_map;
   76 
   77 #define LKM_SPACE_ALLOC(size)           uvm_km_alloc(lkm_map, (size))
   78 #define LKM_SPACE_FREE(addr, size)      uvm_km_free(lkm_map, (addr), (size))
   79 
   80 #if !defined(DEBUG) && defined(LKMDEBUG)
   81 # define DEBUG
   82 #endif
   83 
   84 #ifdef DEBUG
   85 # define LKMDB_INFO     0x01
   86 # define LKMDB_LOAD     0x02
   87 int     lkmdebug = 0;
   88 #endif
   89 
   90 #define PAGESIZE 1024           /* kmem_alloc() allocation quantum */
   91 
   92 #define LKM_ALLOC       0x01
   93 
   94 #define LKMS_IDLE       0x00
   95 #define LKMS_RESERVED   0x01
   96 #define LKMS_LOADING    0x02
   97 #define LKMS_UNLOADING  0x08
   98 
   99 static int      lkm_v = 0;
  100 static int      lkm_state = LKMS_IDLE;
  101 
  102 #ifndef MAXLKMS
  103 #define MAXLKMS         20
  104 #endif
  105 
  106 static struct lkm_table lkmods[MAXLKMS];        /* table of loaded modules */
  107 static struct lkm_table *curp;                  /* global for in-progress ops */
  108 
  109 static void lkmunreserve(void);
  110 static int _lkm_syscall(struct lkm_table *, int);
  111 static int _lkm_vfs(struct lkm_table *, int);
  112 static int _lkm_dev(struct lkm_table *, int);
  113 #ifdef STREAMS
  114 static int _lkm_strmod(struct lkm_table *, int);
  115 #endif
  116 static int _lkm_exec(struct lkm_table *, int);
  117 static int _lkm_compat(struct lkm_table *, int);
  118 
  119 static int _lkm_checkver(struct lkm_table *);
  120 
  121 dev_type_open(lkmopen);
  122 dev_type_close(lkmclose);
  123 dev_type_ioctl(lkmioctl);
  124 
  125 const struct cdevsw lkm_cdevsw = {
  126         lkmopen, lkmclose, noread, nowrite, lkmioctl,
  127         nostop, notty, nopoll, nommap, nokqfilter,
  128 };
  129 
  130 void
  131 lkm_init(void)
  132 {
  133         /*
  134          * If machine-dependent code hasn't initialized the lkm_map
  135          * then just use kernel_map.
  136          */
  137         if (lkm_map == NULL)
  138                 lkm_map = kernel_map;
  139 }
  140 
  141 /*ARGSUSED*/
  142 int
  143 lkmopen(dev, flag, devtype, p)
  144         dev_t dev;
  145         int flag;
  146         int devtype;
  147         struct proc *p;
  148 {
  149         int error;
  150 
  151         if (minor(dev) != 0)
  152                 return (ENXIO);         /* bad minor # */
  153 
  154         /*
  155          * Use of the loadable kernel module device must be exclusive; we
  156          * may try to remove this restriction later, but it's really no
  157          * hardship.
  158          */
  159         while (lkm_v & LKM_ALLOC) {
  160                 if (flag & FNONBLOCK)           /* don't hang */
  161                         return (EBUSY);
  162                 /*
  163                  * Sleep pending unlock; we use tsleep() to allow
  164                  * an alarm out of the open.
  165                  */
  166                 error = tsleep((caddr_t)&lkm_v, TTIPRI|PCATCH, "lkmopn", 0);
  167                 if (error)
  168                         return (error);
  169         }
  170         lkm_v |= LKM_ALLOC;
  171 
  172         return (0);             /* pseudo-device open */
  173 }
  174 
  175 /*
  176  * Unreserve the memory associated with the current loaded module; done on
  177  * a coerced close of the lkm device (close on premature exit of modload)
  178  * or explicitly by modload as a result of a link failure.
  179  */
  180 static void
  181 lkmunreserve()
  182 {
  183 
  184         if (lkm_state == LKMS_IDLE)
  185                 return;
  186 
  187         if (curp && curp->syms) {
  188                 ksyms_delsymtab(curp->private.lkm_any->lkm_name);
  189                 uvm_km_free(kernel_map, curp->syms, curp->sym_size);/**/
  190                 curp->syms = 0;
  191         }
  192         /*
  193          * Actually unreserve the memory
  194          */
  195         if (curp && curp->area) {
  196                 LKM_SPACE_FREE(curp->area, curp->size);
  197                 curp->area = 0;
  198         }
  199 
  200         if (curp && curp->forced)
  201                 curp->forced = 0;
  202 
  203         lkm_state = LKMS_IDLE;
  204 }
  205 
  206 int
  207 lkmclose(dev, flag, mode, p)
  208         dev_t dev;
  209         int flag;
  210         int mode;
  211         struct proc *p;
  212 {
  213 
  214         if (!(lkm_v & LKM_ALLOC)) {
  215 #ifdef DEBUG
  216                 if (lkmdebug & LKMDB_INFO)
  217                         printf("LKM: close before open!\n");
  218 #endif  /* DEBUG */
  219                 return (EBADF);
  220         }
  221 
  222         /* do this before waking the herd... */
  223         if (curp && !curp->used) {
  224                 /*
  225                  * If we close before setting used, we have aborted
  226                  * by way of error or by way of close-on-exit from
  227                  * a premature exit of "modload".
  228                  */
  229                 lkmunreserve(); /* coerce state to LKM_IDLE */
  230         }
  231 
  232         lkm_v &= ~LKM_ALLOC;
  233         wakeup((caddr_t)&lkm_v);        /* thundering herd "problem" here */
  234 
  235         return (0);             /* pseudo-device closed */
  236 }
  237 
  238 /*ARGSUSED*/
  239 int
  240 lkmioctl(dev, cmd, data, flag, p)
  241         dev_t dev;
  242         u_long cmd;
  243         caddr_t data;
  244         int flag;
  245         struct proc *p;
  246 {
  247         int error = 0;
  248         int i;
  249         struct lmc_resrv *resrvp;
  250         struct lmc_loadbuf *loadbufp;
  251         struct lmc_unload *unloadp;
  252         struct lmc_stat  *statp;
  253         char istr[MAXLKMNAME];
  254 
  255         switch(cmd) {
  256         case LMRESERV:          /* reserve pages for a module */
  257                 if (securelevel > 0)
  258                         return EPERM;
  259 
  260                 if ((flag & FWRITE) == 0) /* only allow this if writing */
  261                         return EPERM;
  262 
  263                 resrvp = (struct lmc_resrv *)data;
  264 
  265                 /*
  266                  * Find a free slot.
  267                  */
  268                 for (i = 0; i < MAXLKMS; i++)
  269                         if (!lkmods[i].used)
  270                                 break;
  271                 if (i == MAXLKMS) {
  272                         error = ENOMEM;         /* no slots available */
  273                         break;
  274                 }
  275                 curp = &lkmods[i];
  276 
  277                 resrvp->slot = i;               /* return slot */
  278 
  279                 /*
  280                  * Get memory for module
  281                  */
  282                 curp->size = resrvp->size;
  283 
  284                 curp->area = LKM_SPACE_ALLOC(curp->size);
  285 
  286                 curp->offset = 0;               /* load offset */
  287 
  288                 resrvp->addr = curp->area; /* ret kernel addr */
  289 
  290                 if (cmd == LMRESERV && resrvp->sym_size) {
  291                         curp->sym_size = resrvp->sym_size;
  292                         curp->sym_symsize = resrvp->sym_symsize;
  293                         curp->syms = (u_long) LKM_SPACE_ALLOC(curp->sym_size);
  294                         curp->sym_offset = 0;
  295                         resrvp->sym_addr = curp->syms; /* ret symbol addr */
  296                 } else {
  297                         curp->sym_size = 0;
  298                         curp->syms = 0;
  299                         curp->sym_offset = 0;
  300                         if (cmd == LMRESERV)
  301                                 resrvp->sym_addr = 0;
  302                 }
  303 
  304 #ifdef DEBUG
  305                 if (lkmdebug & LKMDB_INFO) {
  306                         printf("LKM: LMRESERV (actual   = 0x%08lx)\n",
  307                             curp->area);
  308                         printf("LKM: LMRESERV (syms     = 0x%08lx)\n",
  309                                curp->syms);
  310                         printf("LKM: LMRESERV (adjusted = 0x%08lx)\n",
  311                             trunc_page(curp->area));
  312                 }
  313 #endif /* DEBUG */
  314                 lkm_state = LKMS_RESERVED;
  315                 break;
  316 
  317         case LMLOADBUF:         /* Copy in; stateful, follows LMRESERV */
  318                 if (securelevel > 0)
  319                         return EPERM;
  320 
  321                 if ((flag & FWRITE) == 0) /* only allow this if writing */
  322                         return EPERM;
  323 
  324                 loadbufp = (struct lmc_loadbuf *)data;
  325                 i = loadbufp->cnt;
  326                 if ((lkm_state != LKMS_RESERVED && lkm_state != LKMS_LOADING)
  327                     || i < 0
  328                     || i > MODIOBUF
  329                     || i > curp->size - curp->offset) {
  330                         error = ENOMEM;
  331                         break;
  332                 }
  333 
  334                 /* copy in buffer full of data */
  335                 error = copyin(loadbufp->data,
  336                                (caddr_t)curp->area + curp->offset, i);
  337                 if (error)
  338                         break;
  339 
  340                 if ((curp->offset + i) < curp->size) {
  341                         lkm_state = LKMS_LOADING;
  342 #ifdef DEBUG
  343                         if (lkmdebug & LKMDB_LOAD)
  344                 printf("LKM: LMLOADBUF (loading @ %ld of %ld, i = %d)\n",
  345                             curp->offset, curp->size, i);
  346 #endif /* DEBUG */
  347                 }
  348                 curp->offset += i;
  349                 break;
  350 
  351         case LMLOADSYMS:        /* Copy in; stateful, follows LMRESERV*/
  352                 if ((flag & FWRITE) == 0) /* only allow this if writing */
  353                         return EPERM;
  354 
  355                 loadbufp = (struct lmc_loadbuf *)data;
  356                 i = loadbufp->cnt;
  357                 if ((lkm_state != LKMS_LOADING)
  358                     || i < 0
  359                     || i > MODIOBUF
  360                     || i > curp->sym_size - curp->sym_offset) {
  361                         error = ENOMEM;
  362                         break;
  363                 }
  364 
  365                 /* copy in buffer full of data*/
  366                 if ((error = copyin(loadbufp->data,
  367                                    (caddr_t)(curp->syms) + curp->sym_offset,
  368                                    i)) != 0)
  369                         break;
  370 
  371                 if ((curp->sym_offset + i) < curp->sym_size) {
  372                         lkm_state = LKMS_LOADING;
  373 #ifdef DEBUG
  374                         if (lkmdebug & LKMDB_LOAD)
  375                 printf( "LKM: LMLOADSYMS (loading @ %ld of %ld, i = %d)\n",
  376                         curp->sym_offset, curp->sym_size, i);
  377 #endif  /* DEBUG*/
  378                 }
  379                 curp->sym_offset += i;
  380                 break;
  381 
  382         case LMUNRESRV:         /* discard reserved pages for a module */
  383                 if (securelevel > 0)
  384                         return EPERM;
  385 
  386                 if ((flag & FWRITE) == 0) /* only allow this if writing */
  387                         return EPERM;
  388 
  389                 lkmunreserve(); /* coerce state to LKM_IDLE */
  390 #ifdef DEBUG
  391                 if (lkmdebug & LKMDB_INFO)
  392                         printf("LKM: LMUNRESERV\n");
  393 #endif /* DEBUG */
  394                 break;
  395 
  396         case LMREADY:           /* module loaded: call entry */
  397                 if (securelevel > 0)
  398                         return EPERM;
  399 
  400                 if ((flag & FWRITE) == 0) /* only allow this if writing */
  401                         return EPERM;
  402 
  403                 if (lkm_state != LKMS_LOADING) {
  404 #ifdef DEBUG
  405                         if (lkmdebug & LKMDB_INFO)
  406                                 printf("lkm_state is %02x\n", lkm_state);
  407 #endif /* DEBUG */
  408                         return ENXIO;
  409                 }
  410 
  411                 if (curp->size - curp->offset > 0)
  412                         /* The remainder must be bss, so we clear it */
  413                         memset((caddr_t)curp->area + curp->offset, 0,
  414                                curp->size - curp->offset);
  415 
  416                 if (curp->syms && curp->sym_offset >= curp->sym_size) {
  417                         error = ksyms_addsymtab("/lkmtemp/",
  418                             (char *)curp->syms, curp->sym_symsize,
  419                             (char *)curp->syms + curp->sym_symsize,
  420                             curp->sym_size - curp->sym_symsize);
  421                         if (error)
  422                                 break;
  423 #ifdef DEBUG
  424                         if (lkmdebug & LKMDB_INFO)
  425                                 printf( "DDB symbols added!\n" );
  426 #endif
  427                 }
  428 
  429                 curp->entry = (int (*)(struct lkm_table *, int, int))
  430                                 (*((long *) (data)));
  431 
  432                 /* call entry(load)... (assigns "private" portion) */
  433                 error = (*(curp->entry))(curp, LKM_E_LOAD, LKM_VERSION);
  434                 if (curp->syms && curp->sym_offset >= curp->sym_size) {
  435                         ksyms_delsymtab("/lkmtemp/");
  436                         error = ksyms_addsymtab(curp->private.lkm_any->lkm_name,
  437                             (char *)curp->syms, curp->sym_symsize,
  438                             (char *)curp->syms + curp->sym_symsize,
  439                             curp->sym_size - curp->sym_symsize);
  440                 }
  441                 if (error) {
  442                         /*
  443                          * Module may refuse loading or may have a
  444                          * version mismatch...
  445                          */
  446                         lkm_state = LKMS_UNLOADING;     /* for lkmunreserve */
  447                         lkmunreserve();                 /* free memory */
  448                         curp->used = 0;                 /* free slot */
  449 #ifdef DEBUG
  450                         if (lkmdebug & LKMDB_INFO)
  451                                 printf("lkm entry point failed with error %d\n",
  452                                    error);
  453 #endif /* DEBUG */
  454                         break;
  455                 }
  456 
  457                 curp->used = 1;
  458 #ifdef DEBUG
  459                 if (lkmdebug & LKMDB_INFO)
  460                         printf("LKM: LMREADY\n");
  461 #endif /* DEBUG */
  462                 lkm_state = LKMS_IDLE;
  463                 break;
  464 
  465         case LMUNLOAD:          /* unload a module */
  466                 if (securelevel > 0)
  467                         return EPERM;
  468 
  469                 if ((flag & FWRITE) == 0) /* only allow this if writing */
  470                         return EPERM;
  471 
  472                 unloadp = (struct lmc_unload *)data;
  473 
  474                 if ((i = unloadp->id) == -1) {          /* unload by name */
  475                         /*
  476                          * Copy name and lookup id from all loaded
  477                          * modules.  May fail.
  478                          */
  479                         error = copyinstr(unloadp->name, istr, MAXLKMNAME-1,
  480                                           NULL);
  481                         if (error)
  482                                 break;
  483 
  484                         /*
  485                          * look up id...
  486                          */
  487                         for (i = 0; i < MAXLKMS; i++) {
  488                                 if (!lkmods[i].used)
  489                                         continue;
  490                                 if (!strcmp(istr,
  491                                         lkmods[i].private.lkm_any->lkm_name))
  492                                         break;
  493                         }
  494                 }
  495 
  496                 /*
  497                  * Range check the value; on failure, return EINVAL
  498                  */
  499                 if (i < 0 || i >= MAXLKMS) {
  500                         error = EINVAL;
  501                         break;
  502                 }
  503 
  504                 curp = &lkmods[i];
  505 
  506                 if (!curp->used) {
  507                         error = ENOENT;
  508                         break;
  509                 }
  510 
  511                 /* call entry(unload) */
  512                 if ((*(curp->entry))(curp, LKM_E_UNLOAD, LKM_VERSION)) {
  513                         error = EBUSY;
  514                         break;
  515                 }
  516 
  517                 lkm_state = LKMS_UNLOADING;     /* non-idle for lkmunreserve */
  518                 lkmunreserve();                 /* free memory */
  519                 curp->used = 0;                 /* free slot */
  520                 break;
  521 
  522         case LMSTAT:            /* stat a module by id/name */
  523                 /* allow readers and writers to stat */
  524 
  525                 statp = (struct lmc_stat *)data;
  526 
  527                 if ((i = statp->id) == -1) {            /* stat by name */
  528                         /*
  529                          * Copy name and lookup id from all loaded
  530                          * modules.
  531                          */
  532                         copystr(statp->name, istr, MAXLKMNAME-1, (size_t *)0);
  533                         /*
  534                          * look up id...
  535                          */
  536                         for (i = 0; i < MAXLKMS; i++) {
  537                                 if (!lkmods[i].used)
  538                                         continue;
  539                                 if (!strcmp(istr,
  540                                         lkmods[i].private.lkm_any->lkm_name))
  541                                         break;
  542                         }
  543 
  544                         if (i == MAXLKMS) {             /* Not found */
  545                                 error = ENOENT;
  546                                 break;
  547                         }
  548                 }
  549 
  550                 /*
  551                  * Range check the value; on failure, return EINVAL
  552                  */
  553                 if (i < 0 || i >= MAXLKMS) {
  554                         error = EINVAL;
  555                         break;
  556                 }
  557 
  558                 curp = &lkmods[i];
  559 
  560                 if (!curp->used) {                      /* Not found */
  561                         error = ENOENT;
  562                         break;
  563                 }
  564 
  565                 if ((error = (*curp->entry)(curp, LKM_E_STAT, LKM_VERSION)))
  566                         break;
  567 
  568                 /*
  569                  * Copy out stat information for this module...
  570                  */
  571                 statp->id       = i;
  572                 statp->offset   = curp->private.lkm_any->lkm_offset;
  573                 statp->type     = curp->private.lkm_any->lkm_type;
  574                 statp->area     = curp->area;
  575                 statp->size     = curp->size / PAGESIZE;
  576                 statp->private  = (unsigned long)curp->private.lkm_any;
  577                 statp->ver      = LKM_VERSION;
  578                 copystr(curp->private.lkm_any->lkm_name,
  579                           statp->name,
  580                           MAXLKMNAME - 2,
  581                           (size_t *)0);
  582 
  583                 break;
  584 
  585 #ifdef LMFORCE
  586         case LMFORCE:           /* stateful, optionally follows LMRESERV */
  587                 if (securelevel > 0)
  588                         return EPERM;
  589 
  590                 if ((flag & FWRITE) == 0) /* only allow this if writing */
  591                         return EPERM;
  592 
  593                 if (lkm_state != LKMS_RESERVED) {
  594                         error = EPERM;
  595                         break;
  596                 }
  597 
  598                 curp->forced = (*(u_long *)data != 0);
  599                 break;
  600 #endif /* LMFORCE */
  601 
  602         default:                /* bad ioctl()... */
  603                 error = ENOTTY;
  604                 break;
  605         }
  606 
  607         return (error);
  608 }
  609 
  610 /*
  611  * Acts like "nosys" but can be identified in sysent for dynamic call
  612  * number assignment for a limited number of calls.
  613  *
  614  * Place holder for system call slots reserved for loadable modules.
  615  */
  616 int
  617 sys_lkmnosys(l, v, retval)
  618         struct lwp *l;
  619         void *v;
  620         register_t *retval;
  621 {
  622 
  623         return (sys_nosys(l, v, retval));
  624 }
  625 
  626 /*
  627  * A placeholder function for load/unload/stat calls; simply returns zero.
  628  * Used where people don't want to specify a special function.
  629  */
  630 int
  631 lkm_nofunc(lkmtp, cmd)
  632         struct lkm_table *lkmtp;
  633         int cmd;
  634 {
  635 
  636         return (0);
  637 }
  638 
  639 int
  640 lkmexists(lkmtp)
  641         struct lkm_table *lkmtp;
  642 {
  643         int i;
  644 
  645         /*
  646          * see if name exists...
  647          */
  648         for (i = 0; i < MAXLKMS; i++) {
  649                 /*
  650                  * An unused module and the one we are testing are not
  651                  * considered.
  652                  */
  653                 if (!lkmods[i].used || &lkmods[i] == lkmtp)
  654                         continue;
  655                 if (!strcmp(lkmtp->private.lkm_any->lkm_name,
  656                         lkmods[i].private.lkm_any->lkm_name))
  657                         return (1);             /* already loaded... */
  658         }
  659 
  660         return (0);             /* module not loaded... */
  661 }
  662 
  663 /*
  664  * For the loadable system call described by the structure pointed to
  665  * by lkmtp, load/unload/stat it depending on the cmd requested.
  666  */
  667 static int
  668 _lkm_syscall(lkmtp, cmd)
  669         struct lkm_table *lkmtp;
  670         int cmd;
  671 {
  672         struct lkm_syscall *args = lkmtp->private.lkm_syscall;
  673         int i;
  674         int error = 0;
  675 
  676         switch(cmd) {
  677         case LKM_E_LOAD:
  678                 /* don't load twice! */
  679                 if (lkmexists(lkmtp))
  680                         return (EEXIST);
  681 
  682                 if ((i = args->mod.lkm_offset) == -1) { /* auto */
  683                         /*
  684                          * Search the table looking for a slot...
  685                          */
  686                         for (i = 0; i < SYS_MAXSYSCALL; i++)
  687                                 if (sysent[i].sy_call == sys_lkmnosys)
  688                                         break;          /* found it! */
  689                         /* out of allocable slots? */
  690                         if (i == SYS_MAXSYSCALL) {
  691                                 error = ENFILE;
  692                                 break;
  693                         }
  694                 } else {                                /* assign */
  695                         if (i < 0 || i >= SYS_MAXSYSCALL) {
  696                                 error = EINVAL;
  697                                 break;
  698                         }
  699                 }
  700 
  701                 /* save old */
  702                 memcpy(&args->lkm_oldent, &sysent[i], sizeof(struct sysent));
  703 
  704                 /* replace with new */
  705                 memcpy(&sysent[i], args->lkm_sysent, sizeof(struct sysent));
  706 
  707                 /* done! */
  708                 args->mod.lkm_offset = i;       /* slot in sysent[] */
  709 
  710                 break;
  711 
  712         case LKM_E_UNLOAD:
  713                 /* current slot... */
  714                 i = args->mod.lkm_offset;
  715 
  716                 /* replace current slot contents with old contents */
  717                 memcpy(&sysent[i], &args->lkm_oldent, sizeof(struct sysent));
  718 
  719                 break;
  720 
  721         case LKM_E_STAT:        /* no special handling... */
  722                 break;
  723         }
  724 
  725         return (error);
  726 }
  727 
  728 /*
  729  * For the loadable virtual file system described by the structure pointed
  730  * to by lkmtp, load/unload/stat it depending on the cmd requested.
  731  */
  732 static int
  733 _lkm_vfs(lkmtp, cmd)
  734         struct lkm_table *lkmtp;
  735         int cmd;
  736 {
  737         struct lkm_vfs *args = lkmtp->private.lkm_vfs;
  738         int error = 0;
  739 
  740         switch(cmd) {
  741         case LKM_E_LOAD:
  742                 /* don't load twice! */
  743                 if (lkmexists(lkmtp))
  744                         return (EEXIST);
  745 
  746                 /* Establish the file system. */
  747                 if ((error = vfs_attach(args->lkm_vfsops)) != 0)
  748                         return (error);
  749 
  750                 /* done! */
  751                 break;
  752 
  753         case LKM_E_UNLOAD:
  754                 /* Disestablish the file system. */
  755                 if ((error = vfs_detach(args->lkm_vfsops)) != 0)
  756                         return (error);
  757                 break;
  758 
  759         case LKM_E_STAT:        /* no special handling... */
  760                 break;
  761         }
  762 
  763         return (error);
  764 }
  765 
  766 /*
  767  * For the loadable device driver described by the structure pointed to
  768  * by lkmtp, load/unload/stat it depending on the cmd requested.
  769  */
  770 static int
  771 _lkm_dev(lkmtp, cmd)
  772         struct lkm_table *lkmtp;
  773         int cmd;
  774 {
  775         struct lkm_dev *args = lkmtp->private.lkm_dev;
  776         int error;
  777 
  778         switch(cmd) {
  779         case LKM_E_LOAD:
  780                 /* don't load twice! */
  781                 if (lkmexists(lkmtp))
  782                         return (EEXIST);
  783 
  784                 error = devsw_attach(args->lkm_devname,
  785                                      args->lkm_bdev, &args->lkm_bdevmaj,
  786                                      args->lkm_cdev, &args->lkm_cdevmaj);
  787                 if (error != 0)
  788                         return (error);
  789 
  790                 args->mod.lkm_offset =
  791                         LKM_MAKEMAJOR(args->lkm_bdevmaj, args->lkm_cdevmaj);
  792                 break;
  793 
  794         case LKM_E_UNLOAD:
  795                 devsw_detach(args->lkm_bdev, args->lkm_cdev);
  796                 args->lkm_bdevmaj = -1;
  797                 args->lkm_cdevmaj = -1;
  798                 break;
  799 
  800         case LKM_E_STAT:        /* no special handling... */
  801                 break;
  802         }
  803 
  804         return (0);
  805 }
  806 
  807 #ifdef STREAMS
  808 /*
  809  * For the loadable streams module described by the structure pointed to
  810  * by lkmtp, load/unload/stat it depending on the cmd requested.
  811  */
  812 static int
  813 _lkm_strmod(lkmtp, cmd)
  814         struct lkm_table *lkmtp;
  815         int cmd;
  816 {
  817         struct lkm_strmod *args = lkmtp->private.lkm_strmod;
  818         int i;
  819         int error = 0;
  820 
  821         switch(cmd) {
  822         case LKM_E_LOAD:
  823                 /* don't load twice! */
  824                 if (lkmexists(lkmtp))
  825                         return (EEXIST);
  826                 break;
  827 
  828         case LKM_E_UNLOAD:
  829                 break;
  830 
  831         case LKM_E_STAT:        /* no special handling... */
  832                 break;
  833         }
  834 
  835         return (error);
  836 }
  837 #endif  /* STREAMS */
  838 
  839 /*
  840  * For the loadable execution class described by the structure pointed to
  841  * by lkmtp, load/unload/stat it depending on the cmd requested.
  842  */
  843 static int
  844 _lkm_exec(lkmtp, cmd)
  845         struct lkm_table *lkmtp;
  846         int cmd;
  847 {
  848         struct lkm_exec *args = lkmtp->private.lkm_exec;
  849         int error = 0;
  850 
  851         switch(cmd) {
  852         case LKM_E_LOAD:
  853                 /* don't load twice! */
  854                 if (lkmexists(lkmtp))
  855                         return (EEXIST);
  856 
  857                 /* this would also fill in the emulation pointer in
  858                  * args->lkm_execsw */
  859                 error = exec_add(args->lkm_execsw, args->lkm_emul);
  860                 break;
  861 
  862         case LKM_E_UNLOAD:
  863                 error = exec_remove(args->lkm_execsw);
  864                 break;
  865 
  866         case LKM_E_STAT:        /* no special handling... */
  867                 break;
  868         }
  869 
  870         return (error);
  871 }
  872 
  873 /*
  874  * For the loadable compat/emulation class described by the structure pointed to
  875  * by lkmtp, load/unload/stat it depending on the cmd requested.
  876  */
  877 static int
  878 _lkm_compat(lkmtp, cmd)
  879         struct lkm_table *lkmtp;
  880         int cmd;
  881 {
  882         struct lkm_compat *args = lkmtp->private.lkm_compat;
  883         int error = 0;
  884 
  885         switch(cmd) {
  886         case LKM_E_LOAD:
  887                 /* don't load twice! */
  888                 if (lkmexists(lkmtp))
  889                         return (EEXIST);
  890 
  891                 error = emul_register(args->lkm_compat, 0);
  892                 break;
  893 
  894         case LKM_E_UNLOAD:
  895                 error = emul_unregister(args->lkm_compat->e_name);
  896                 break;
  897 
  898         case LKM_E_STAT:        /* no special handling... */
  899                 break;
  900         }
  901 
  902         return (error);
  903 }
  904 
  905 /*
  906  * This code handles the per-module type "wiring-in" of loadable modules
  907  * into existing kernel tables.  For "LM_MISC" modules, wiring and unwiring
  908  * is assumed to be done in their entry routines internal to the module
  909  * itself.
  910  */
  911 int
  912 lkmdispatch(lkmtp, cmd)
  913         struct lkm_table *lkmtp;
  914         int cmd;
  915 {
  916         int error = 0;          /* default = success */
  917 #ifdef DEBUG
  918         if (lkmdebug & LKMDB_INFO)
  919                 printf( "lkmdispatch: %p %d\n", lkmtp, cmd );
  920 #endif
  921 
  922         /* If loading, check the LKM is compatible */
  923         if (cmd == LKM_E_LOAD) {
  924                 if (_lkm_checkver(lkmtp))
  925                         return (EPROGMISMATCH);
  926         }
  927 
  928         switch(lkmtp->private.lkm_any->lkm_type) {
  929         case LM_SYSCALL:
  930                 error = _lkm_syscall(lkmtp, cmd);
  931                 break;
  932 
  933         case LM_VFS:
  934                 error = _lkm_vfs(lkmtp, cmd);
  935                 break;
  936 
  937         case LM_DEV:
  938                 error = _lkm_dev(lkmtp, cmd);
  939                 break;
  940 
  941 #ifdef STREAMS
  942         case LM_STRMOD:
  943             {
  944                 struct lkm_strmod *args = lkmtp->private.lkm_strmod;
  945             }
  946                 break;
  947 
  948 #endif  /* STREAMS */
  949 
  950         case LM_EXEC:
  951                 error = _lkm_exec(lkmtp, cmd);
  952                 break;
  953 
  954         case LM_COMPAT:
  955                 error = _lkm_compat(lkmtp, cmd);
  956                 break;
  957 
  958         case LM_MISC:   /* ignore content -- no "misc-specific" procedure */
  959                 break;
  960 
  961         default:
  962                 error = ENXIO;  /* unknown type */
  963                 break;
  964         }
  965 
  966         return (error);
  967 }
  968 
  969 /*
  970  * Check LKM version against current kernel.
  971  */
  972 static int
  973 _lkm_checkver(struct lkm_table *lkmtp)
  974 {
  975         struct lkm_any *mod = lkmtp->private.lkm_any;
  976 
  977         if (mod->lkm_modver != LKM_VERSION) {
  978                 printf("LKM '%s': LKM version mismatch - LKM %d, kernel %d\n",
  979                     mod->lkm_name, mod->lkm_modver, LKM_VERSION);
  980                 return (1);
  981         }
  982 
  983         if (lkmtp->forced) {
  984                 printf("LKM '%s': forced load, skipping compatibility checks\n",
  985                     mod->lkm_name);
  986                 return (0);
  987         }
  988 
  989         if (mod->lkm_sysver != __NetBSD_Version__) {
  990                 printf("LKM '%s': kernel version mismatch - LKM %d, kernel %d\n",
  991                     mod->lkm_name, mod->lkm_sysver, __NetBSD_Version__);
  992                 return (2);
  993         }
  994 
  995         /*
  996          * Following might eventually be changed to take into account envdep,
  997          * if it's non-NULL.
  998          */
  999         if (strcmp(mod->lkm_envver, _LKM_ENV_VERSION) != 0) {
 1000                 const char *kenv = _LKM_ENV_VERSION;
 1001                 const char *envver = mod->lkm_envver;
 1002 
 1003                 if (kenv[0] == ',')
 1004                         kenv++;
 1005                 if (envver[0] == ',')
 1006                         envver++;
 1007 
 1008                 printf("LKM '%s': environment compile options mismatch - LKM '%s', kernel '%s'\n",
 1009                     mod->lkm_name, envver, kenv);
 1010                 return (3);
 1011         }
 1012 
 1013         /*
 1014          * Basic parameters match, LKM is hopefully compatible.
 1015          * Cross fingers and approve.
 1016          */
 1017         return (0);
 1018 }

Cache object: 43c93a7f08d658c512d099bce2e5797e


[ 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.