diff --git a/sys/kern/kern_lkm.c b/sys/kern/kern_lkm.c index 06f3e5b72803..ad78d287ece5 100644 --- a/sys/kern/kern_lkm.c +++ b/sys/kern/kern_lkm.c @@ -1,991 +1,995 @@ /*- * Copyright (c) 1992 Terrence R. Lambert. * Copyright (c) 1994 Christopher G. Demetriou * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Terrence R. Lambert. * 4. The name Terrence R. Lambert may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY TERRENCE R. LAMBERT ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: kern_lkm.c,v 1.17 1995/11/06 00:35:50 bde Exp $ + * $Id: kern_lkm.c,v 1.18 1995/11/09 09:43:32 bde Exp $ */ /* * XXX it's not really safe to unload *any* of the types which are * currently loadable; e.g. you could unload a syscall which was being * blocked in, etc. In the long term, a solution should be come up * with, but "not right now." -- cgd */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PAGESIZE 1024 /* kmem_alloc() allocation quantum */ #define LKM_ALLOC 0x01 #define LKM_WANT 0x02 #define LKMS_IDLE 0x00 #define LKMS_RESERVED 0x01 #define LKMS_LOADING 0x02 #define LKMS_LOADED 0x04 #define LKMS_UNLOADING 0x08 static int lkm_v = 0; static int lkm_state = LKMS_IDLE; #ifndef MAXLKMS #define MAXLKMS 20 #endif static struct lkm_table lkmods[MAXLKMS]; /* table of loaded modules */ static struct lkm_table *curp; /* global for in-progress ops */ static int _lkm_dev __P((struct lkm_table *lkmtp, int cmd)); static int _lkm_exec __P((struct lkm_table *lkmtp, int cmd)); static int _lkm_vfs __P((struct lkm_table *lkmtp, int cmd)); static int _lkm_syscall __P((struct lkm_table *lkmtp, int cmd)); static void lkmunreserve __P((void)); /*ARGSUSED*/ int lkmcopen(dev, flag, devtype, p) dev_t dev; int flag; int devtype; struct proc *p; { int error; if (minor(dev) != 0) return(ENXIO); /* bad minor # */ /* * Use of the loadable kernel module device must be exclusive; we * may try to remove this restriction later, but it's really no * hardship. */ while (lkm_v & LKM_ALLOC) { if (flag & FNONBLOCK) /* don't hang */ return(EBUSY); lkm_v |= LKM_WANT; /* * Sleep pending unlock; we use tsleep() to allow * an alarm out of the open. */ error = tsleep((caddr_t)&lkm_v, TTIPRI|PCATCH, "lkmopn", 0); if (error) return(error); /* leave LKM_WANT set -- no problem */ } lkm_v |= LKM_ALLOC; return(0); /* pseudo-device open */ } /* * Unreserve the memory associated with the current loaded module; done on * a coerced close of the lkm device (close on premature exit of modload) * or explicitly by modload as a result of a link failure. */ static void lkmunreserve() { if (lkm_state == LKMS_IDLE) return; /* * Actually unreserve the memory */ if (curp && curp->area) { kmem_free(kmem_map, curp->area, curp->size);/**/ curp->area = 0; if (curp->private.lkm_any != NULL) curp->private.lkm_any = NULL; } lkm_state = LKMS_IDLE; } int lkmcclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { if (!(lkm_v & LKM_ALLOC)) { #ifdef DEBUG printf("LKM: close before open!\n"); #endif /* DEBUG */ return(EBADF); } /* do this before waking the herd... */ if (curp && !curp->used) { /* * If we close before setting used, we have aborted * by way of error or by way of close-on-exit from * a premature exit of "modload". */ lkmunreserve(); /* coerce state to LKM_IDLE */ } lkm_v &= ~LKM_ALLOC; wakeup((caddr_t)&lkm_v); /* thundering herd "problem" here */ return(0); /* pseudo-device closed */ } /*ARGSUSED*/ int lkmcioctl(dev, cmd, data, flag, p) dev_t dev; int cmd; caddr_t data; int flag; struct proc *p; { int err = 0; int i; struct lmc_resrv *resrvp; struct lmc_loadbuf *loadbufp; struct lmc_unload *unloadp; struct lmc_stat *statp; char istr[MAXLKMNAME]; switch(cmd) { case LMRESERV: /* reserve pages for a module */ if ((flag & FWRITE) == 0) /* only allow this if writing */ return EPERM; resrvp = (struct lmc_resrv *)data; /* * Find a free slot. */ for (i = 0; i < MAXLKMS; i++) if (!lkmods[i].used) break; if (i == MAXLKMS) { err = ENOMEM; /* no slots available */ break; } curp = &lkmods[i]; curp->id = i; /* self reference slot offset */ resrvp->slot = i; /* return slot */ /* * Get memory for module */ curp->size = resrvp->size; curp->area = kmem_alloc(kmem_map, curp->size);/**/ curp->offset = 0; /* load offset */ resrvp->addr = curp->area; /* ret kernel addr */ #ifdef DEBUG printf("LKM: LMRESERV (actual = 0x%08x)\n", curp->area); printf("LKM: LMRESERV (adjusted = 0x%08x)\n", trunc_page(curp->area)); #endif /* DEBUG */ lkm_state = LKMS_RESERVED; break; case LMLOADBUF: /* Copy in; stateful, follows LMRESERV */ if ((flag & FWRITE) == 0) /* only allow this if writing */ return EPERM; loadbufp = (struct lmc_loadbuf *)data; i = loadbufp->cnt; if ((lkm_state != LKMS_RESERVED && lkm_state != LKMS_LOADING) || i < 0 || i > MODIOBUF || i > curp->size - curp->offset) { err = ENOMEM; break; } /* copy in buffer full of data */ err = copyin((caddr_t)loadbufp->data, (caddr_t)curp->area + curp->offset, i); if (err) break; if ((curp->offset + i) < curp->size) { lkm_state = LKMS_LOADING; #ifdef DEBUG printf("LKM: LMLOADBUF (loading @ %d of %d, i = %d)\n", curp->offset, curp->size, i); #endif /* DEBUG */ } else { lkm_state = LKMS_LOADED; #ifdef DEBUG printf("LKM: LMLOADBUF (loaded)\n"); #endif /* DEBUG */ } curp->offset += i; break; case LMUNRESRV: /* discard reserved pages for a module */ if ((flag & FWRITE) == 0) /* only allow this if writing */ return EPERM; lkmunreserve(); /* coerce state to LKM_IDLE */ #ifdef DEBUG printf("LKM: LMUNRESERV\n"); #endif /* DEBUG */ break; case LMREADY: /* module loaded: call entry */ if ((flag & FWRITE) == 0) /* only allow this if writing */ return EPERM; switch (lkm_state) { case LKMS_LOADED: break; case LKMS_LOADING: /* The remainder must be bss, so we clear it */ bzero((caddr_t)curp->area + curp->offset, curp->size - curp->offset); break; default: #ifdef DEBUG printf("lkm_state is %02x\n", lkm_state); #endif /* DEBUG */ return ENXIO; } /* XXX gack */ curp->entry = (int (*) __P((struct lkm_table *, int, int))) (*((int *)data)); /* call entry(load)... (assigns "private" portion) */ err = (*(curp->entry))(curp, LKM_E_LOAD, LKM_VERSION); if (err) { /* * Module may refuse loading or may have a * version mismatch... */ lkm_state = LKMS_UNLOADING; /* for lkmunreserve */ lkmunreserve(); /* free memory */ curp->used = 0; /* free slot */ break; } /* * It's possible for a user to load a module that doesn't * initialize itself correctly. (You can even get away with * using it for a while.) Unfortunately, we are faced with * the following problems: * - we can't tell a good module from a bad one until * after we've run its entry function (if the private * section is uninitalized after we return from the * entry, then something's fishy) * - now that we've called the entry function, we can't * forcibly unload the module without risking a crash * - since we don't know what the module's entry function * did, we can't easily clean up the mess it may have * made, so we can't know just how unstable the system * may be * So, being stuck between a rock and a hard place, we * have no choice but to do this... */ if (curp->private.lkm_any == NULL) panic("loadable module initialization failed"); curp->used = 1; #ifdef DEBUG printf("LKM: LMREADY\n"); #endif /* DEBUG */ lkm_state = LKMS_IDLE; break; case LMUNLOAD: /* unload a module */ if ((flag & FWRITE) == 0) /* only allow this if writing */ return EPERM; unloadp = (struct lmc_unload *)data; if ((i = unloadp->id) == -1) { /* unload by name */ /* * Copy name and lookup id from all loaded * modules. May fail. */ err =copyinstr(unloadp->name, istr, MAXLKMNAME-1, NULL); if (err) break; /* * look up id... */ for (i = 0; i < MAXLKMS; i++) { if (!lkmods[i].used) continue; if (!strcmp(istr, lkmods[i].private.lkm_any->lkm_name)) break; } } /* * Range check the value; on failure, return EINVAL */ if (i < 0 || i >= MAXLKMS) { err = EINVAL; break; } curp = &lkmods[i]; if (!curp->used) { err = ENOENT; break; } /* call entry(unload) */ if ((*(curp->entry))(curp, LKM_E_UNLOAD, LKM_VERSION)) { err = EBUSY; break; } lkm_state = LKMS_UNLOADING; /* non-idle for lkmunreserve */ lkmunreserve(); /* free memory */ curp->used = 0; /* free slot */ break; case LMSTAT: /* stat a module by id/name */ /* allow readers and writers to stat */ statp = (struct lmc_stat *)data; if ((i = statp->id) == -1) { /* stat by name */ /* * Copy name and lookup id from all loaded * modules. */ copystr(statp->name, istr, MAXLKMNAME-1, NULL); /* * look up id... */ for (i = 0; i < MAXLKMS; i++) { if (!lkmods[i].used) continue; if (!strcmp(istr, lkmods[i].private.lkm_any->lkm_name)) break; } if (i == MAXLKMS) { /* Not found */ err = ENOENT; break; } } /* * Range check the value; on failure, return EINVAL */ if (i < 0 || i >= MAXLKMS) { err = EINVAL; break; } curp = &lkmods[i]; if (!curp->used) { /* Not found */ err = ENOENT; break; } /* * Copy out stat information for this module... */ statp->id = curp->id; statp->offset = curp->private.lkm_any->lkm_offset; statp->type = curp->private.lkm_any->lkm_type; statp->area = curp->area; statp->size = curp->size / PAGESIZE; statp->private = (unsigned long)curp->private.lkm_any; statp->ver = curp->private.lkm_any->lkm_ver; copystr(curp->private.lkm_any->lkm_name, statp->name, MAXLKMNAME - 2, NULL); break; default: /* bad ioctl()... */ err = ENOTTY; break; } return (err); } /* * Acts like "nosys" but can be identified in sysent for dynamic call * number assignment for a limited number of calls. * * Place holder for system call slots reserved for loadable modules. */ int lkmnosys(p, args, retval) struct proc *p; struct nosys_args *args; int *retval; { return(nosys(p, args, retval)); } /* * Acts like "enodev", but can be identified in cdevsw and bdevsw for * dynamic driver major number assignment for a limited number of * drivers. * * Place holder for device switch slots reserved for loadable modules. */ int lkmenodev(dev, flags, fmt, p) dev_t dev; int flags; int fmt; struct proc *p; { return(ENODEV); } int lkmexists(lkmtp) struct lkm_table *lkmtp; { int i; /* * see if name exists... */ for (i = 0; i < MAXLKMS; i++) { /* * An unused module and the one we are testing are not * considered. */ if (!lkmods[i].used || &lkmods[i] == lkmtp) continue; if (!strcmp(lkmtp->private.lkm_any->lkm_name, lkmods[i].private.lkm_any->lkm_name)) return(1); /* already loaded... */ } return(0); /* module not loaded... */ } /* * For the loadable system call described by the structure pointed to * by lkmtp, load/unload/stat it depending on the cmd requested. */ static int _lkm_syscall(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; { struct lkm_syscall *args = lkmtp->private.lkm_syscall; int i; int err = 0; switch(cmd) { case LKM_E_LOAD: /* don't load twice! */ if (lkmexists(lkmtp)) return(EEXIST); if ((i = args->lkm_offset) == -1) { /* auto */ /* * Search the table looking for a slot... */ for (i = 0; i < aout_sysvec.sv_size; i++) if (aout_sysvec.sv_table[i].sy_call == (sy_call_t *)lkmnosys) break; /* found it! */ /* out of allocable slots? */ if (i == aout_sysvec.sv_size) { err = ENFILE; break; } } else { /* assign */ if (i < 0 || i >= aout_sysvec.sv_size) { err = EINVAL; break; } } /* save old */ bcopy(&aout_sysvec.sv_table[i], &(args->lkm_oldent), sizeof(struct sysent)); /* replace with new */ bcopy(args->lkm_sysent, &aout_sysvec.sv_table[i], sizeof(struct sysent)); /* done! */ args->lkm_offset = i; /* slot in sysent[] */ break; case LKM_E_UNLOAD: /* current slot... */ i = args->lkm_offset; /* replace current slot contents with old contents */ bcopy(&(args->lkm_oldent), &aout_sysvec.sv_table[i], sizeof(struct sysent)); break; case LKM_E_STAT: /* no special handling... */ break; } return(err); } /* * For the loadable virtual file system described by the structure pointed * to by lkmtp, load/unload/stat it depending on the cmd requested. */ static int _lkm_vfs(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; { struct lkm_vfs *args = lkmtp->private.lkm_vfs; struct vfsconf *vfc = args->lkm_vfsconf; int i; int err = 0; switch(cmd) { case LKM_E_LOAD: /* don't load twice! */ if (lkmexists(lkmtp)) return(EEXIST); for(i = 0; i < MOUNT_MAXTYPE; i++) { if(!strcmp(vfc->vfc_name, vfsconf[i]->vfc_name)) { return EEXIST; } } i = args->lkm_offset = vfc->vfc_index; if (i < 0) { for (i = MOUNT_MAXTYPE - 1; i >= 0; i--) { if(vfsconf[i] == &void_vfsconf) break; } } if (i < 0) { return EINVAL; } args->lkm_offset = vfc->vfc_index = i; vfsconf[i] = vfc; vfssw[i] = vfc->vfc_vfsops; /* like in vfs_op_init */ for(i = 0; args->lkm_vnodeops->ls_items[i]; i++) { struct vnodeopv_desc *opv = (struct vnodeopv_desc *)args->lkm_vnodeops->ls_items[i]; *(opv->opv_desc_vector_p) = NULL; } vfs_opv_init((struct vnodeopv_desc **)args->lkm_vnodeops->ls_items); /* * Call init function for this VFS... */ (*(vfssw[vfc->vfc_index]->vfs_init))(); /* done! */ break; case LKM_E_UNLOAD: /* current slot... */ i = args->lkm_offset; if (vfsconf[i]->vfc_refcount) { return EBUSY; } /* replace current slot contents with old contents */ vfssw[i] = (struct vfsops *)0; vfsconf[i] = &void_vfsconf; break; case LKM_E_STAT: /* no special handling... */ break; } return(err); } /* * For the loadable device driver described by the structure pointed to * by lkmtp, load/unload/stat it depending on the cmd requested. */ static int _lkm_dev(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; { struct lkm_dev *args = lkmtp->private.lkm_dev; int i; #ifdef JREMOD dev_t descrip; #endif /* JREMOD */ int err = 0; switch(cmd) { case LKM_E_LOAD: /* don't load twice! */ if (lkmexists(lkmtp)) return(EEXIST); switch(args->lkm_devtype) { case LM_DT_BLOCK: #ifndef JREMOD if ((i = args->lkm_offset) == -1) { /* auto */ /* * Search the table looking for a slot... */ for (i = 0; i < nblkdev; i++) if (bdevsw[i].d_open == lkmenodev) break; /* found it! */ /* out of allocable slots? */ if (i == nblkdev) { err = ENFILE; break; } } else { /* assign */ if (i < 0 || i >= nblkdev) { err = EINVAL; break; } } /* save old */ bcopy(&bdevsw[i], &(args->lkm_olddev.bdev), sizeof(struct bdevsw)); /* replace with new */ bcopy(args->lkm_dev.bdev, &bdevsw[i], sizeof(struct bdevsw)); /* done! */ args->lkm_offset = i; /* slot in bdevsw[] */ #else /* JREMOD */ if ((i = args->lkm_offset) == -1) descrip = (dev_t) -1; else descrip = makedev(args->lkm_offset,0); if ( err = bdevsw_add(&descrip, args->lkm_dev.bdev, &(args->lkm_olddev.bdev))) { break; } args->lkm_offset = major(descrip) ; #endif /* JREMOD */ break; case LM_DT_CHAR: #ifndef JREMOD if ((i = args->lkm_offset) == -1) { /* auto */ /* * Search the table looking for a slot... */ for (i = 0; i < nchrdev; i++) if (cdevsw[i].d_open == lkmenodev) break; /* found it! */ /* out of allocable slots? */ if (i == nchrdev) { err = ENFILE; break; } } else { /* assign */ if (i < 0 || i >= nchrdev) { err = EINVAL; break; } } /* save old */ bcopy(&cdevsw[i], &(args->lkm_olddev.cdev), sizeof(struct cdevsw)); /* replace with new */ bcopy(args->lkm_dev.cdev, &cdevsw[i], sizeof(struct cdevsw)); /* done! */ args->lkm_offset = i; /* slot in cdevsw[] */ #else /* JREMOD */ if ((i = args->lkm_offset) == -1) descrip = (dev_t) -1; else descrip = makedev(args->lkm_offset,0); if ( err = cdevsw_add(&descrip, args->lkm_dev.cdev, &(args->lkm_olddev.cdev))) { break; } args->lkm_offset = major(descrip) ; #endif /* JREMOD */ break; default: err = ENODEV; break; } break; case LKM_E_UNLOAD: /* current slot... */ i = args->lkm_offset; switch(args->lkm_devtype) { case LM_DT_BLOCK: /* replace current slot contents with old contents */ #ifndef JREMOD bcopy(&(args->lkm_olddev.bdev), &bdevsw[i], sizeof(struct bdevsw)); #else /* JREMOD */ descrip = makedev(i,0); bdevsw_add(&descrip, &(args->lkm_olddev.bdev),NULL); #endif /* JREMOD */ break; case LM_DT_CHAR: /* replace current slot contents with old contents */ #ifndef JREMOD bcopy(&(args->lkm_olddev.cdev), &cdevsw[i], sizeof(struct cdevsw)); #else /* JREMOD */ descrip = makedev(i,0); cdevsw_add(&descrip, &(args->lkm_olddev.cdev),NULL); #endif /* JREMOD */ break; default: err = ENODEV; break; } break; case LKM_E_STAT: /* no special handling... */ break; } return(err); } #ifdef STREAMS /* * For the loadable streams module described by the structure pointed to * by lkmtp, load/unload/stat it depending on the cmd requested. */ static int _lkm_strmod(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; { struct lkm_strmod *args = lkmtp->private.lkm_strmod; int i; int err = 0; switch(cmd) { case LKM_E_LOAD: /* don't load twice! */ if (lkmexists(lkmtp)) return(EEXIST); break; case LKM_E_UNLOAD: break; case LKM_E_STAT: /* no special handling... */ break; } return(err); } #endif /* STREAMS */ /* * For the loadable execution class described by the structure pointed to * by lkmtp, load/unload/stat it depending on the cmd requested. */ static int _lkm_exec(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; { struct lkm_exec *args = lkmtp->private.lkm_exec; int i; int err = 0; const struct execsw **execsw = (const struct execsw **)&execsw_set.ls_items[0]; #if 1 switch(cmd) { case LKM_E_LOAD: /* don't load twice! */ if (lkmexists(lkmtp)) return(EEXIST); if ((i = args->lkm_offset) == -1) { /* auto */ /* * Search the table looking for a slot... */ for (i = 0; execsw[i] != NULL; i++) if (execsw[i]->ex_imgact == NULL) break; /* found it! */ /* out of allocable slots? */ if (execsw[i] == NULL) { err = ENFILE; break; } } else { /* assign */ err = EINVAL; break; } /* save old */ bcopy(&execsw[i], &(args->lkm_oldexec), sizeof(struct execsw*)); /* replace with new */ bcopy(&(args->lkm_exec), &execsw[i], sizeof(struct execsw*)); /* done! */ args->lkm_offset = i; /* slot in execsw[] */ break; case LKM_E_UNLOAD: /* current slot... */ i = args->lkm_offset; /* replace current slot contents with old contents */ bcopy(&(args->lkm_oldexec), &execsw[i], sizeof(struct execsw*)); break; case LKM_E_STAT: /* no special handling... */ break; } #else err = EINVAL; #endif return(err); } -static const struct execsw lkm_exec_dummy = { NULL, "lkm" }; -TEXT_SET(execsw_set, lkm_exec_dummy); -TEXT_SET(execsw_set, lkm_exec_dummy); -TEXT_SET(execsw_set, lkm_exec_dummy); -TEXT_SET(execsw_set, lkm_exec_dummy); +/* XXX: This is bogus. we should find a better method RSN! */ +static const struct execsw lkm_exec_dummy1 = { NULL, "lkm" }; +static const struct execsw lkm_exec_dummy2 = { NULL, "lkm" }; +static const struct execsw lkm_exec_dummy3 = { NULL, "lkm" }; +static const struct execsw lkm_exec_dummy4 = { NULL, "lkm" }; +TEXT_SET(execsw_set, lkm_exec_dummy1); +TEXT_SET(execsw_set, lkm_exec_dummy2); +TEXT_SET(execsw_set, lkm_exec_dummy3); +TEXT_SET(execsw_set, lkm_exec_dummy4); /* * This code handles the per-module type "wiring-in" of loadable modules * into existing kernel tables. For "LM_MISC" modules, wiring and unwiring * is assumed to be done in their entry routines internal to the module * itself. */ int lkmdispatch(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; { int err = 0; /* default = success */ switch(lkmtp->private.lkm_any->lkm_type) { case LM_SYSCALL: err = _lkm_syscall(lkmtp, cmd); break; case LM_VFS: err = _lkm_vfs(lkmtp, cmd); break; case LM_DEV: err = _lkm_dev(lkmtp, cmd); break; #ifdef STREAMS case LM_STRMOD: { struct lkm_strmod *args = lkmtp->private.lkm_strmod; } break; #endif /* STREAMS */ case LM_EXEC: err = _lkm_exec(lkmtp, cmd); break; case LM_MISC: /* ignore content -- no "misc-specific" procedure */ if (lkmexists(lkmtp)) err = EEXIST; break; default: err = ENXIO; /* unknown type */ break; } return(err); } int lkm_nullcmd(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; { return (0); } diff --git a/sys/sys/kernel.h b/sys/sys/kernel.h index 428091e0d497..05e7548b4fda 100644 --- a/sys/sys/kernel.h +++ b/sys/sys/kernel.h @@ -1,266 +1,268 @@ /*- * Copyright (c) 1995 Terrence R. Lambert * All rights reserved. * * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)kernel.h 8.3 (Berkeley) 1/21/94 - * $Id: kernel.h,v 1.14 1995/11/13 07:18:09 bde Exp $ + * $Id: kernel.h,v 1.15 1995/11/14 07:35:56 bde Exp $ */ #ifndef _SYS_KERNEL_H_ #define _SYS_KERNEL_H_ /* Global variables for the kernel. */ /* 1.1 */ extern long hostid; extern char hostname[MAXHOSTNAMELEN]; extern int hostnamelen; extern char domainname[MAXHOSTNAMELEN]; extern int domainnamelen; extern char kernelname[MAXPATHLEN]; /* 1.2 */ extern volatile struct timeval mono_time; extern struct timeval boottime; extern struct timeval runtime; extern volatile struct timeval time; extern struct timezone tz; /* XXX */ extern int tick; /* usec per tick (1000000 / hz) */ extern int hz; /* system clock's frequency */ extern int stathz; /* statistics clock's frequency */ extern int profhz; /* profiling clock's frequency */ extern int ticks; extern int lbolt; /* once a second sleep address */ extern int tickdelta; extern long timedelta; /* * The following macros are used to declare global sets of objects, which * are collected by the linker into a `struct linker_set' as defined below. * * NB: the constants defined below must match those defined in * ld/ld.h. Since their calculation requires arithmetic, we * can't name them symbolically (e.g., 23 is N_SETT | N_EXT). */ #define MAKE_SET(set, sym, type) \ + static void *const __set_##set##_sym_##sym = \ + (&__set_##set##_sym_##sym, &sym, 0); \ asm(".stabs \"_" #set "\", " #type ", 0, 0, _" #sym) #define TEXT_SET(set, sym) MAKE_SET(set, sym, 23) #define DATA_SET(set, sym) MAKE_SET(set, sym, 25) #define BSS_SET(set, sym) MAKE_SET(set, sym, 27) #define ABS_SET(set, sym) MAKE_SET(set, sym, 21) /* * Enumerated types for known system startup interfaces. * * Startup occurs in ascending numeric order; the list entries are * sorted prior to attempting startup to guarantee order. Items * of the same level are arbitrated for order based on the 'order' * element. * * These numbers are arbitrary and are chosen ONLY for ordering; the * enumeration values are explicit rather than imlicit to provide * for binary compatability with inserted elements. * * The SI_SUB_RUN_SCHEDULER value must have the highest lexical value. * * The SI_SUB_CONSOLE and SI_SUB_SWAP values represent values used by * the BSD 4.4Lite but not by FreeBSD; they are maintained in dependent * order to support porting. * * The SI_SUB_PROTO_BEGIN and SI_SUB_PROTO_END bracket a range of * initializations to take place at splimp(). This is a historical * wart that should be removed -- probably running everything at * splimp() until the first init that doesn't want it is the correct * fix. They are currently present to ensure historical behaviour. */ enum sysinit_sub_id { SI_SUB_DUMMY = 0x00000000, /* not executed; for linker*/ SI_SUB_CONSOLE = 0x08000000, /* console*/ SI_SUB_COPYRIGHT = 0x08000001, /* first use of console*/ SI_SUB_VM = 0x10000000, /* virtual memory system init*/ SI_SUB_KMEM = 0x18000000, /* kernel memory*/ SI_SUB_CPU = 0x20000000, /* CPU resource(s)*/ SI_SUB_DEVFS = 0x22000000, /* get DEVFS ready */ SI_SUB_CONFIGURE = 0x24000000, /* Configure devices */ SI_SUB_INTRINSIC = 0x28000000, /* proc 0*/ SI_SUB_RUN_QUEUE = 0x30000000, /* the run queue*/ SI_SUB_VM_CONF = 0x38000000, /* config VM, set limits*/ SI_SUB_VFS = 0x40000000, /* virtual file system*/ SI_SUB_CLOCKS = 0x48000000, /* real time and stat clocks*/ SI_SUB_MBUF = 0x50000000, /* mbufs*/ SI_SUB_CLIST = 0x58000000, /* clists*/ SI_SUB_SYSV_SHM = 0x64000000, /* System V shared memory*/ SI_SUB_SYSV_SEM = 0x68000000, /* System V semaphores*/ SI_SUB_SYSV_MSG = 0x6C000000, /* System V message queues*/ SI_SUB_PSEUDO = 0x70000000, /* pseudo devices*/ SI_SUB_PROTO_BEGIN = 0x80000000, /* XXX: set splimp (kludge)*/ SI_SUB_PROTO_IF = 0x84000000, /* interfaces*/ SI_SUB_PROTO_DOMAIN = 0x88000000, /* domains (address families?)*/ SI_SUB_PROTO_END = 0x8fffffff, /* XXX: set splx (kludge)*/ SI_SUB_KPROF = 0x90000000, /* kernel profiling*/ SI_SUB_KICK_SCHEDULER = 0xa0000000, /* start the timeout events*/ SI_SUB_ROOT = 0xb0000000, /* root mount*/ SI_SUB_ROOT_FDTAB = 0xb8000000, /* root vnode in fd table...*/ SI_SUB_SWAP = 0xc0000000, /* swap*/ SI_SUB_INTRINSIC_POST = 0xd0000000, /* proc 0 cleanup*/ SI_SUB_KTHREAD_INIT = 0xe0000000, /* init process*/ SI_SUB_KTHREAD_PAGE = 0xe4000000, /* pageout daemon*/ SI_SUB_KTHREAD_VM = 0xe8000000, /* vm daemon*/ SI_SUB_KTHREAD_UPDATE = 0xec000000, /* update daemon*/ SI_SUB_RUN_SCHEDULER = 0xffffffff /* scheduler: no return*/ }; /* * Some enumerated orders; "ANY" sorts last. */ enum sysinit_elem_order { SI_ORDER_FIRST = 0x00000000, /* first*/ SI_ORDER_SECOND = 0x00000001, /* second*/ SI_ORDER_THIRD = 0x00000002, /* third*/ SI_ORDER_ANY = 0xffffffff /* last*/ }; /* * System initialization call types; currently two are supported... one * to do a simple function call and one to cause a process to be started * by the kernel on the callers behalf. */ typedef enum sysinit_elem_type { SI_TYPE_DEFAULT = 0x00000000, /* No special processing*/ SI_TYPE_KTHREAD = 0x00000001 /* start kernel thread*/ } si_elem_t; /* * A system initialization call instance * * The subsystem */ struct sysinit { unsigned int subsystem; /* subsystem identifier*/ unsigned int order; /* init order within subsystem*/ void (*func) __P((void *)); /* init function*/ void *udata; /* multiplexer/argument*/ si_elem_t type; /* sysinit_elem_type*/ }; /* * Default: no special processing */ #define SYSINIT(uniquifier, subsystem, order, func, ident) \ static struct sysinit uniquifier ## _sys_init = { \ subsystem, \ order, \ func, \ ident, \ SI_TYPE_DEFAULT \ }; \ DATA_SET(sysinit_set,uniquifier ## _sys_init); /* * Call 'fork()' before calling '(*func)(ident)'; * for making a kernel 'thread' (or builtin process.) */ #define SYSINIT_KT(uniquifier, subsystem, order, func, ident) \ static struct sysinit uniquifier ## _sys_init = { \ subsystem, \ order, \ func, \ ident, \ SI_TYPE_KTHREAD \ }; \ DATA_SET(sysinit_set,uniquifier ## _sys_init); /* * A kernel process descriptor; used to start "internal" daemons * * Note: global_procpp may be NULL for no global save area */ struct kproc_desc { char *arg0; /* arg 0 (for 'ps' listing)*/ void (*func) __P((void)); /* "main" for kernel process*/ struct proc **global_procpp; /* ptr to proc ptr save area*/ }; /* init_proc.c*/ extern void kproc_start __P((void *udata)); #ifdef PSEUDO_LKM #include #include #include #include #define PSEUDO_SET(init, name) \ extern struct linker_set MODVNOPS; \ MOD_MISC(name); \ static int \ name ## _load(struct lkm_table *lkmtp, int cmd) \ { init((void *)NULL /* XXX unused (?) */); return 0; } \ static int \ name ## _unload(struct lkm_table *lkmtp, int cmd) \ { return EINVAL; } \ int \ name ## _mod(struct lkm_table *lkmtp, int cmd, int ver) { \ DISPATCH(lkmtp, cmd, ver, name ## _load, name ## _unload, \ lkm_nullcmd); } #else /* PSEUDO_LKM */ /* * Compatability. To be deprecated after LKM is updated. */ #define PSEUDO_SET(sym, name) SYSINIT(ps, SI_SUB_PSEUDO, SI_ORDER_ANY, sym, 0) #endif /* PSEUDO_LKM */ struct linker_set { int ls_length; caddr_t ls_items[1]; /* really ls_length of them, trailing NULL */ }; extern const struct linker_set execsw_set; #endif /* _SYS_KERNEL_H_*/