2009-12-29 20:49:21 +03:00
|
|
|
/* $NetBSD: kern_module.c,v 1.55 2009/12/29 17:49:21 elad Exp $ */
|
2008-01-16 15:34:50 +03:00
|
|
|
|
|
|
|
/*-
|
|
|
|
* Copyright (c) 2008 The NetBSD Foundation, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
2008-11-12 15:35:50 +03:00
|
|
|
* This code is derived from software developed for The NetBSD Foundation
|
|
|
|
* by Andrew Doran.
|
|
|
|
*
|
2008-01-16 15:34:50 +03:00
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2008-01-16 21:28:32 +03:00
|
|
|
* Kernel module support.
|
2008-01-16 15:34:50 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
2009-12-29 20:49:21 +03:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: kern_module.c,v 1.55 2009/12/29 17:49:21 elad Exp $");
|
2009-11-18 20:40:45 +03:00
|
|
|
|
|
|
|
#define _MODULE_INTERNAL
|
2008-11-25 18:14:07 +03:00
|
|
|
|
|
|
|
#ifdef _KERNEL_OPT
|
|
|
|
#include "opt_ddb.h"
|
2009-02-14 01:41:00 +03:00
|
|
|
#include "opt_modular.h"
|
2008-11-25 18:14:07 +03:00
|
|
|
#endif
|
2008-01-16 15:34:50 +03:00
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
2008-11-15 02:06:45 +03:00
|
|
|
#include <sys/kernel.h>
|
2008-01-16 15:34:50 +03:00
|
|
|
#include <sys/proc.h>
|
|
|
|
#include <sys/kauth.h>
|
|
|
|
#include <sys/kobj.h>
|
|
|
|
#include <sys/kmem.h>
|
|
|
|
#include <sys/module.h>
|
2008-05-20 21:24:56 +04:00
|
|
|
#include <sys/kauth.h>
|
2008-11-15 02:06:45 +03:00
|
|
|
#include <sys/kthread.h>
|
2008-12-03 15:14:11 +03:00
|
|
|
#include <sys/sysctl.h>
|
2009-06-07 13:47:31 +04:00
|
|
|
#include <sys/lock.h>
|
2008-01-16 15:34:50 +03:00
|
|
|
|
|
|
|
#include <uvm/uvm_extern.h>
|
|
|
|
|
|
|
|
#include <machine/stdarg.h>
|
|
|
|
|
2008-11-12 15:35:50 +03:00
|
|
|
struct vm_map *module_map;
|
2009-11-18 20:40:45 +03:00
|
|
|
char module_base[MODULE_BASE_SIZE];
|
2008-01-16 21:28:32 +03:00
|
|
|
|
2008-01-16 15:34:50 +03:00
|
|
|
struct modlist module_list = TAILQ_HEAD_INITIALIZER(module_list);
|
|
|
|
struct modlist module_bootlist = TAILQ_HEAD_INITIALIZER(module_bootlist);
|
2008-05-01 21:23:16 +04:00
|
|
|
static module_t *module_active;
|
2008-12-03 15:14:11 +03:00
|
|
|
static int module_verbose_on;
|
|
|
|
static int module_autoload_on = 1;
|
2008-01-16 15:34:50 +03:00
|
|
|
u_int module_count;
|
|
|
|
kmutex_t module_lock;
|
2008-11-15 02:06:45 +03:00
|
|
|
u_int module_autotime = 10;
|
|
|
|
u_int module_gen = 1;
|
|
|
|
static kcondvar_t module_thread_cv;
|
|
|
|
static kmutex_t module_thread_lock;
|
|
|
|
static int module_thread_ticks;
|
2008-01-16 15:34:50 +03:00
|
|
|
|
2009-10-03 04:06:37 +04:00
|
|
|
static kauth_listener_t module_listener;
|
|
|
|
|
2008-01-16 15:34:50 +03:00
|
|
|
/* Ensure that the kernel's link set isn't empty. */
|
|
|
|
static modinfo_t module_dummy;
|
|
|
|
__link_set_add_rodata(modules, module_dummy);
|
|
|
|
|
2008-03-02 14:18:43 +03:00
|
|
|
static int module_do_load(const char *, bool, int, prop_dictionary_t,
|
2008-05-20 23:20:38 +04:00
|
|
|
module_t **, modclass_t class, bool);
|
2008-01-16 15:34:50 +03:00
|
|
|
static int module_do_unload(const char *);
|
|
|
|
static int module_do_builtin(const char *, module_t **);
|
2008-05-01 18:44:48 +04:00
|
|
|
static int module_fetch_info(module_t *);
|
2008-11-15 02:06:45 +03:00
|
|
|
static void module_thread(void *);
|
2009-11-18 20:40:45 +03:00
|
|
|
|
2009-06-09 23:09:03 +04:00
|
|
|
static bool module_merge_dicts(prop_dictionary_t, const prop_dictionary_t);
|
2008-01-16 15:34:50 +03:00
|
|
|
|
2009-11-18 20:40:45 +03:00
|
|
|
int module_eopnotsupp(void);
|
|
|
|
|
|
|
|
int
|
|
|
|
module_eopnotsupp(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
__weak_alias(module_load_vfs,module_eopnotsupp);
|
|
|
|
|
2008-01-16 15:34:50 +03:00
|
|
|
/*
|
|
|
|
* module_error:
|
|
|
|
*
|
|
|
|
* Utility function: log an error.
|
|
|
|
*/
|
2009-11-18 20:40:45 +03:00
|
|
|
void
|
2008-01-16 15:34:50 +03:00
|
|
|
module_error(const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
printf("WARNING: module error: ");
|
|
|
|
vprintf(fmt, ap);
|
|
|
|
printf("\n");
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2008-12-03 15:14:11 +03:00
|
|
|
/*
|
|
|
|
* module_print:
|
|
|
|
*
|
|
|
|
* Utility function: log verbose output.
|
|
|
|
*/
|
2009-11-18 20:40:45 +03:00
|
|
|
void
|
2008-12-03 15:14:11 +03:00
|
|
|
module_print(const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
if (module_verbose_on) {
|
|
|
|
va_start(ap, fmt);
|
|
|
|
printf("DEBUG: module: ");
|
|
|
|
vprintf(fmt, ap);
|
|
|
|
printf("\n");
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-29 20:49:21 +03:00
|
|
|
static int
|
|
|
|
module_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
|
|
|
|
void *arg0, void *arg1, void *arg2, void *arg3)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
|
|
|
|
result = KAUTH_RESULT_DEFER;
|
|
|
|
|
|
|
|
if (action != KAUTH_SYSTEM_MODULE)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
if ((uintptr_t)arg2 != 0) /* autoload */
|
|
|
|
result = KAUTH_RESULT_ALLOW;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2008-01-16 15:34:50 +03:00
|
|
|
/*
|
|
|
|
* module_init:
|
|
|
|
*
|
|
|
|
* Initialize the module subsystem.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
module_init(void)
|
|
|
|
{
|
2008-11-12 15:35:50 +03:00
|
|
|
extern struct vm_map *module_map;
|
2008-01-16 15:34:50 +03:00
|
|
|
|
2008-11-12 15:35:50 +03:00
|
|
|
if (module_map == NULL) {
|
|
|
|
module_map = kernel_map;
|
|
|
|
}
|
2008-01-16 15:34:50 +03:00
|
|
|
mutex_init(&module_lock, MUTEX_DEFAULT, IPL_NONE);
|
2008-11-15 02:06:45 +03:00
|
|
|
cv_init(&module_thread_cv, "modunload");
|
|
|
|
mutex_init(&module_thread_lock, MUTEX_DEFAULT, IPL_NONE);
|
2008-05-02 16:59:34 +04:00
|
|
|
#ifdef MODULAR /* XXX */
|
2008-05-01 18:44:48 +04:00
|
|
|
module_init_md();
|
2008-05-01 21:23:16 +04:00
|
|
|
#endif
|
2008-05-20 18:11:55 +04:00
|
|
|
|
|
|
|
#if __NetBSD_Version__ / 1000000 % 100 == 99 /* -current */
|
2009-01-25 01:14:44 +03:00
|
|
|
snprintf(module_base, sizeof(module_base), "/stand/%s/%s/modules",
|
2008-05-20 20:04:08 +04:00
|
|
|
machine, osrelease);
|
2008-05-20 18:11:55 +04:00
|
|
|
#else /* release */
|
2009-01-25 01:14:44 +03:00
|
|
|
snprintf(module_base, sizeof(module_base), "/stand/%s/%d.%d/modules",
|
2008-05-20 20:04:08 +04:00
|
|
|
machine, __NetBSD_Version__ / 100000000,
|
2008-05-20 18:11:55 +04:00
|
|
|
__NetBSD_Version__ / 1000000 % 100);
|
|
|
|
#endif
|
2009-10-03 04:06:37 +04:00
|
|
|
|
2009-12-29 20:49:21 +03:00
|
|
|
module_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM,
|
|
|
|
module_listener_cb, NULL);
|
2009-10-03 04:06:37 +04:00
|
|
|
}
|
|
|
|
|
First part of secmodel cleanup and other misc. changes:
- Separate the suser part of the bsd44 secmodel into its own secmodel
and directory, pending even more cleanups. For revision history
purposes, the original location of the files was
src/sys/secmodel/bsd44/secmodel_bsd44_suser.c
src/sys/secmodel/bsd44/suser.h
- Add a man-page for secmodel_suser(9) and update the one for
secmodel_bsd44(9).
- Add a "secmodel" module class and use it. Userland program and
documentation updated.
- Manage secmodel count (nsecmodels) through the module framework.
This eliminates the need for secmodel_{,de}register() calls in
secmodel code.
- Prepare for secmodel modularization by adding relevant module bits.
The secmodels don't allow auto unload. The bsd44 secmodel depends
on the suser and securelevel secmodels. The overlay secmodel depends
on the bsd44 secmodel. As the module class is only cosmetic, and to
prevent ambiguity, the bsd44 and overlay secmodels are prefixed with
"secmodel_".
- Adapt the overlay secmodel to recent changes (mainly vnode scope).
- Stop using link-sets for the sysctl node(s) creation.
- Keep sysctl variables under nodes of their relevant secmodels. In
other words, don't create duplicates for the suser/securelevel
secmodels under the bsd44 secmodel, as the latter is merely used
for "grouping".
- For the suser and securelevel secmodels, "advertise presence" in
relevant sysctl nodes (sysctl.security.models.{suser,securelevel}).
- Get rid of the LKM preprocessor stuff.
- As secmodels are now modules, there's no need for an explicit call
to secmodel_start(); it's handled by the module framework. That
said, the module framework was adjusted to properly load secmodels
early during system startup.
- Adapt rump to changes: Instead of using empty stubs for securelevel,
simply use the suser secmodel. Also replace secmodel_start() with a
call to secmodel_suser_start().
- 5.99.20.
Testing was done on i386 ("release" build). Spearated module_init()
changes were tested on sparc and sparc64 as well by martin@ (thanks!).
Mailing list reference:
http://mail-index.netbsd.org/tech-kern/2009/09/25/msg006135.html
2009-10-02 22:50:12 +04:00
|
|
|
/*
|
|
|
|
* module_init2:
|
|
|
|
*
|
|
|
|
* Start the auto unload kthread.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
module_init2(void)
|
|
|
|
{
|
|
|
|
int error;
|
2008-11-15 02:06:45 +03:00
|
|
|
|
|
|
|
error = kthread_create(PRI_VM, KTHREAD_MPSAFE, NULL, module_thread,
|
|
|
|
NULL, NULL, "modunload");
|
|
|
|
if (error != 0)
|
|
|
|
panic("module_init: %d", error);
|
2008-01-16 15:34:50 +03:00
|
|
|
}
|
|
|
|
|
2008-12-03 15:14:11 +03:00
|
|
|
SYSCTL_SETUP(sysctl_module_setup, "sysctl module setup")
|
|
|
|
{
|
|
|
|
const struct sysctlnode *node = NULL;
|
|
|
|
|
|
|
|
sysctl_createv(clog, 0, NULL, NULL,
|
|
|
|
CTLFLAG_PERMANENT,
|
|
|
|
CTLTYPE_NODE, "kern", NULL,
|
|
|
|
NULL, 0, NULL, 0,
|
|
|
|
CTL_KERN, CTL_EOL);
|
|
|
|
sysctl_createv(clog, 0, NULL, &node,
|
|
|
|
CTLFLAG_PERMANENT,
|
|
|
|
CTLTYPE_NODE, "module",
|
|
|
|
SYSCTL_DESCR("Module options"),
|
|
|
|
NULL, 0, NULL, 0,
|
|
|
|
CTL_KERN, CTL_CREATE, CTL_EOL);
|
|
|
|
|
|
|
|
if (node == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
sysctl_createv(clog, 0, &node, NULL,
|
|
|
|
CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
|
|
|
|
CTLTYPE_INT, "autoload",
|
|
|
|
SYSCTL_DESCR("Enable automatic load of modules"),
|
|
|
|
NULL, 0, &module_autoload_on, 0,
|
|
|
|
CTL_CREATE, CTL_EOL);
|
|
|
|
sysctl_createv(clog, 0, &node, NULL,
|
|
|
|
CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
|
|
|
|
CTLTYPE_INT, "verbose",
|
|
|
|
SYSCTL_DESCR("Enable verbose output"),
|
|
|
|
NULL, 0, &module_verbose_on, 0,
|
|
|
|
CTL_CREATE, CTL_EOL);
|
|
|
|
}
|
|
|
|
|
2008-01-16 15:34:50 +03:00
|
|
|
/*
|
|
|
|
* module_init_class:
|
|
|
|
*
|
|
|
|
* Initialize all built-in and pre-loaded modules of the
|
|
|
|
* specified class.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
module_init_class(modclass_t class)
|
|
|
|
{
|
|
|
|
__link_set_decl(modules, modinfo_t);
|
|
|
|
modinfo_t *const *mip, *mi;
|
|
|
|
module_t *mod;
|
|
|
|
|
|
|
|
mutex_enter(&module_lock);
|
|
|
|
/*
|
|
|
|
* Builtins first. These can't depend on pre-loaded modules.
|
|
|
|
*/
|
|
|
|
__link_set_foreach(mip, modules) {
|
|
|
|
mi = *mip;
|
|
|
|
if (mi == &module_dummy) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (class != MODULE_CLASS_ANY && class != mi->mi_class) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
(void)module_do_builtin(mi->mi_name, NULL);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Now preloaded modules. These will be pulled off the
|
|
|
|
* list as we call module_do_load();
|
|
|
|
*/
|
2008-05-01 18:44:48 +04:00
|
|
|
do {
|
|
|
|
TAILQ_FOREACH(mod, &module_bootlist, mod_chain) {
|
|
|
|
mi = mod->mod_info;
|
|
|
|
if (class != MODULE_CLASS_ANY &&
|
|
|
|
class != mi->mi_class)
|
|
|
|
continue;
|
2008-05-20 21:24:56 +04:00
|
|
|
module_do_load(mi->mi_name, false, 0, NULL, NULL,
|
2008-12-28 06:21:02 +03:00
|
|
|
class, false);
|
2008-05-01 18:44:48 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (mod != NULL);
|
2008-01-16 15:34:50 +03:00
|
|
|
mutex_exit(&module_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2008-06-01 00:14:38 +04:00
|
|
|
* module_compatible:
|
2008-01-16 15:34:50 +03:00
|
|
|
*
|
2008-06-01 00:14:38 +04:00
|
|
|
* Return true if the two supplied kernel versions are said to
|
|
|
|
* have the same binary interface for kernel code. The entire
|
|
|
|
* version is signficant for the development tree (-current),
|
|
|
|
* major and minor versions are significant for official
|
|
|
|
* releases of the system.
|
2008-01-16 15:34:50 +03:00
|
|
|
*/
|
2008-08-01 18:05:15 +04:00
|
|
|
bool
|
2008-06-01 00:14:38 +04:00
|
|
|
module_compatible(int v1, int v2)
|
2008-01-16 15:34:50 +03:00
|
|
|
{
|
|
|
|
|
2008-06-01 00:14:38 +04:00
|
|
|
#if __NetBSD_Version__ / 1000000 % 100 == 99 /* -current */
|
|
|
|
return v1 == v2;
|
|
|
|
#else /* release */
|
|
|
|
return abs(v1 - v2) < 10000;
|
|
|
|
#endif
|
2008-01-16 15:34:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* module_load:
|
|
|
|
*
|
2008-03-02 14:18:43 +03:00
|
|
|
* Load a single module from the file system.
|
2008-01-16 15:34:50 +03:00
|
|
|
*/
|
|
|
|
int
|
2008-05-20 21:24:56 +04:00
|
|
|
module_load(const char *filename, int flags, prop_dictionary_t props,
|
2008-10-22 15:16:29 +04:00
|
|
|
modclass_t class)
|
2008-01-16 15:34:50 +03:00
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
2008-05-20 21:24:56 +04:00
|
|
|
/* Authorize. */
|
|
|
|
error = kauth_authorize_system(kauth_cred_get(), KAUTH_SYSTEM_MODULE,
|
|
|
|
0, (void *)(uintptr_t)MODCTL_LOAD, NULL, NULL);
|
|
|
|
if (error != 0) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2008-01-16 15:34:50 +03:00
|
|
|
mutex_enter(&module_lock);
|
2008-05-20 23:20:38 +04:00
|
|
|
error = module_do_load(filename, false, flags, props, NULL, class,
|
2008-10-22 15:16:29 +04:00
|
|
|
false);
|
2008-01-16 15:34:50 +03:00
|
|
|
mutex_exit(&module_lock);
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2008-10-22 15:16:29 +04:00
|
|
|
/*
|
|
|
|
* module_autoload:
|
|
|
|
*
|
|
|
|
* Load a single module from the file system, system initiated.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
module_autoload(const char *filename, modclass_t class)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
KASSERT(mutex_owned(&module_lock));
|
|
|
|
|
2008-12-03 15:14:11 +03:00
|
|
|
/* Nothing if the user has disabled it. */
|
|
|
|
if (!module_autoload_on) {
|
|
|
|
return EPERM;
|
|
|
|
}
|
|
|
|
|
2008-11-19 16:07:42 +03:00
|
|
|
/* Disallow path seperators and magic symlinks. */
|
|
|
|
if (strchr(filename, '/') != NULL || strchr(filename, '@') != NULL ||
|
|
|
|
strchr(filename, '.') != NULL) {
|
|
|
|
return EPERM;
|
|
|
|
}
|
|
|
|
|
2008-10-22 15:16:29 +04:00
|
|
|
/* Authorize. */
|
|
|
|
error = kauth_authorize_system(kauth_cred_get(), KAUTH_SYSTEM_MODULE,
|
|
|
|
0, (void *)(uintptr_t)MODCTL_LOAD, (void *)(uintptr_t)1, NULL);
|
|
|
|
if (error != 0) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return module_do_load(filename, false, 0, NULL, NULL, class, true);
|
|
|
|
}
|
|
|
|
|
2008-01-16 15:34:50 +03:00
|
|
|
/*
|
|
|
|
* module_unload:
|
|
|
|
*
|
|
|
|
* Find and unload a module by name.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
module_unload(const char *name)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
2008-05-20 21:24:56 +04:00
|
|
|
/* Authorize. */
|
|
|
|
error = kauth_authorize_system(kauth_cred_get(), KAUTH_SYSTEM_MODULE,
|
|
|
|
0, (void *)(uintptr_t)MODCTL_UNLOAD, NULL, NULL);
|
|
|
|
if (error != 0) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2008-01-16 15:34:50 +03:00
|
|
|
mutex_enter(&module_lock);
|
|
|
|
error = module_do_unload(name);
|
|
|
|
mutex_exit(&module_lock);
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* module_lookup:
|
|
|
|
*
|
|
|
|
* Look up a module by name.
|
|
|
|
*/
|
|
|
|
module_t *
|
|
|
|
module_lookup(const char *name)
|
|
|
|
{
|
|
|
|
module_t *mod;
|
|
|
|
|
|
|
|
KASSERT(mutex_owned(&module_lock));
|
|
|
|
|
|
|
|
TAILQ_FOREACH(mod, &module_list, mod_chain) {
|
|
|
|
if (strcmp(mod->mod_info->mi_name, name) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return mod;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* module_hold:
|
|
|
|
*
|
|
|
|
* Add a single reference to a module. It's the caller's
|
|
|
|
* responsibility to ensure that the reference is dropped
|
|
|
|
* later.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
module_hold(const char *name)
|
|
|
|
{
|
|
|
|
module_t *mod;
|
|
|
|
|
|
|
|
mutex_enter(&module_lock);
|
|
|
|
mod = module_lookup(name);
|
|
|
|
if (mod == NULL) {
|
|
|
|
mutex_exit(&module_lock);
|
|
|
|
return ENOENT;
|
|
|
|
}
|
|
|
|
mod->mod_refcnt++;
|
|
|
|
mutex_exit(&module_lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* module_rele:
|
|
|
|
*
|
|
|
|
* Release a reference acquired with module_hold().
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
module_rele(const char *name)
|
|
|
|
{
|
|
|
|
module_t *mod;
|
|
|
|
|
|
|
|
mutex_enter(&module_lock);
|
|
|
|
mod = module_lookup(name);
|
|
|
|
if (mod == NULL) {
|
|
|
|
mutex_exit(&module_lock);
|
|
|
|
panic("module_rele: gone");
|
|
|
|
}
|
|
|
|
mod->mod_refcnt--;
|
|
|
|
mutex_exit(&module_lock);
|
|
|
|
}
|
|
|
|
|
2008-11-18 14:39:41 +03:00
|
|
|
/*
|
|
|
|
* module_enqueue:
|
|
|
|
*
|
|
|
|
* Put a module onto the global list and update counters.
|
|
|
|
*/
|
2009-11-05 17:09:14 +03:00
|
|
|
void
|
2008-11-18 14:39:41 +03:00
|
|
|
module_enqueue(module_t *mod)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2009-11-05 17:09:14 +03:00
|
|
|
KASSERT(mutex_owned(&module_lock));
|
|
|
|
|
2008-11-18 14:39:41 +03:00
|
|
|
/*
|
|
|
|
* If there are requisite modules, put at the head of the queue.
|
|
|
|
* This is so that autounload can unload requisite modules with
|
|
|
|
* only one pass through the queue.
|
|
|
|
*/
|
|
|
|
if (mod->mod_nrequired) {
|
|
|
|
TAILQ_INSERT_HEAD(&module_list, mod, mod_chain);
|
|
|
|
|
|
|
|
/* Add references to the requisite modules. */
|
|
|
|
for (i = 0; i < mod->mod_nrequired; i++) {
|
|
|
|
KASSERT(mod->mod_required[i] != NULL);
|
|
|
|
mod->mod_required[i]->mod_refcnt++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
TAILQ_INSERT_TAIL(&module_list, mod, mod_chain);
|
|
|
|
}
|
|
|
|
module_count++;
|
|
|
|
module_gen++;
|
|
|
|
}
|
|
|
|
|
2008-01-16 15:34:50 +03:00
|
|
|
/*
|
|
|
|
* module_do_builtin:
|
|
|
|
*
|
|
|
|
* Initialize a single module from the list of modules that are
|
|
|
|
* built into the kernel (linked into the kernel image).
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
module_do_builtin(const char *name, module_t **modp)
|
|
|
|
{
|
|
|
|
__link_set_decl(modules, modinfo_t);
|
|
|
|
modinfo_t *const *mip;
|
|
|
|
const char *p, *s;
|
|
|
|
char buf[MAXMODNAME];
|
|
|
|
modinfo_t *mi;
|
|
|
|
module_t *mod, *mod2;
|
|
|
|
size_t len;
|
2008-11-18 14:39:41 +03:00
|
|
|
int error;
|
2008-01-16 15:34:50 +03:00
|
|
|
|
|
|
|
KASSERT(mutex_owned(&module_lock));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check to see if already loaded.
|
|
|
|
*/
|
|
|
|
if ((mod = module_lookup(name)) != NULL) {
|
|
|
|
if (modp != NULL) {
|
|
|
|
*modp = mod;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search the list to see if we have a module by this name.
|
|
|
|
*/
|
|
|
|
error = ENOENT;
|
|
|
|
__link_set_foreach(mip, modules) {
|
|
|
|
mi = *mip;
|
|
|
|
if (mi == &module_dummy) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (strcmp(mi->mi_name, name) == 0) {
|
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (error != 0) {
|
2008-12-28 06:21:02 +03:00
|
|
|
module_error("can't find `%s'", name);
|
2008-01-16 15:34:50 +03:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize pre-requisites.
|
|
|
|
*/
|
|
|
|
mod = kmem_zalloc(sizeof(*mod), KM_SLEEP);
|
|
|
|
if (mod == NULL) {
|
2008-12-28 06:21:02 +03:00
|
|
|
module_error("out of memory for `%s'", name);
|
2008-01-16 15:34:50 +03:00
|
|
|
return ENOMEM;
|
|
|
|
}
|
|
|
|
if (modp != NULL) {
|
|
|
|
*modp = mod;
|
|
|
|
}
|
|
|
|
if (mi->mi_required != NULL) {
|
|
|
|
for (s = mi->mi_required; *s != '\0'; s = p) {
|
|
|
|
if (*s == ',')
|
|
|
|
s++;
|
|
|
|
p = s;
|
|
|
|
while (*p != '\0' && *p != ',')
|
|
|
|
p++;
|
|
|
|
len = min(p - s + 1, sizeof(buf));
|
|
|
|
strlcpy(buf, s, len);
|
|
|
|
if (buf[0] == '\0')
|
|
|
|
break;
|
|
|
|
if (mod->mod_nrequired == MAXMODDEPS - 1) {
|
|
|
|
module_error("too many required modules");
|
|
|
|
kmem_free(mod, sizeof(*mod));
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
error = module_do_builtin(buf, &mod2);
|
|
|
|
if (error != 0) {
|
|
|
|
kmem_free(mod, sizeof(*mod));
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
mod->mod_required[mod->mod_nrequired++] = mod2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to initialize the module.
|
|
|
|
*/
|
2008-05-01 21:23:16 +04:00
|
|
|
KASSERT(module_active == NULL);
|
|
|
|
module_active = mod;
|
2008-01-16 15:34:50 +03:00
|
|
|
error = (*mi->mi_modcmd)(MODULE_CMD_INIT, NULL);
|
2008-05-01 21:23:16 +04:00
|
|
|
module_active = NULL;
|
2008-01-16 15:34:50 +03:00
|
|
|
if (error != 0) {
|
|
|
|
module_error("builtin module `%s' "
|
|
|
|
"failed to init", mi->mi_name);
|
|
|
|
kmem_free(mod, sizeof(*mod));
|
|
|
|
return error;
|
|
|
|
}
|
First part of secmodel cleanup and other misc. changes:
- Separate the suser part of the bsd44 secmodel into its own secmodel
and directory, pending even more cleanups. For revision history
purposes, the original location of the files was
src/sys/secmodel/bsd44/secmodel_bsd44_suser.c
src/sys/secmodel/bsd44/suser.h
- Add a man-page for secmodel_suser(9) and update the one for
secmodel_bsd44(9).
- Add a "secmodel" module class and use it. Userland program and
documentation updated.
- Manage secmodel count (nsecmodels) through the module framework.
This eliminates the need for secmodel_{,de}register() calls in
secmodel code.
- Prepare for secmodel modularization by adding relevant module bits.
The secmodels don't allow auto unload. The bsd44 secmodel depends
on the suser and securelevel secmodels. The overlay secmodel depends
on the bsd44 secmodel. As the module class is only cosmetic, and to
prevent ambiguity, the bsd44 and overlay secmodels are prefixed with
"secmodel_".
- Adapt the overlay secmodel to recent changes (mainly vnode scope).
- Stop using link-sets for the sysctl node(s) creation.
- Keep sysctl variables under nodes of their relevant secmodels. In
other words, don't create duplicates for the suser/securelevel
secmodels under the bsd44 secmodel, as the latter is merely used
for "grouping".
- For the suser and securelevel secmodels, "advertise presence" in
relevant sysctl nodes (sysctl.security.models.{suser,securelevel}).
- Get rid of the LKM preprocessor stuff.
- As secmodels are now modules, there's no need for an explicit call
to secmodel_start(); it's handled by the module framework. That
said, the module framework was adjusted to properly load secmodels
early during system startup.
- Adapt rump to changes: Instead of using empty stubs for securelevel,
simply use the suser secmodel. Also replace secmodel_start() with a
call to secmodel_suser_start().
- 5.99.20.
Testing was done on i386 ("release" build). Spearated module_init()
changes were tested on sparc and sparc64 as well by martin@ (thanks!).
Mailing list reference:
http://mail-index.netbsd.org/tech-kern/2009/09/25/msg006135.html
2009-10-02 22:50:12 +04:00
|
|
|
if (mi->mi_class == MODULE_CLASS_SECMODEL)
|
|
|
|
secmodel_register();
|
2008-01-16 15:34:50 +03:00
|
|
|
mod->mod_info = mi;
|
|
|
|
mod->mod_source = MODULE_SOURCE_KERNEL;
|
2008-11-18 14:39:41 +03:00
|
|
|
module_enqueue(mod);
|
2008-01-16 15:34:50 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* module_do_load:
|
|
|
|
*
|
|
|
|
* Helper routine: load a module from the file system, or one
|
|
|
|
* pushed by the boot loader.
|
|
|
|
*/
|
|
|
|
static int
|
2008-11-18 14:39:41 +03:00
|
|
|
module_do_load(const char *name, bool isdep, int flags,
|
2008-05-20 23:20:38 +04:00
|
|
|
prop_dictionary_t props, module_t **modp, modclass_t class,
|
|
|
|
bool autoload)
|
2008-01-16 15:34:50 +03:00
|
|
|
{
|
|
|
|
static TAILQ_HEAD(,module) pending = TAILQ_HEAD_INITIALIZER(pending);
|
|
|
|
static int depth;
|
|
|
|
const int maxdepth = 6;
|
|
|
|
modinfo_t *mi;
|
|
|
|
module_t *mod, *mod2;
|
2009-06-07 13:47:31 +04:00
|
|
|
prop_dictionary_t filedict;
|
2009-11-18 20:40:45 +03:00
|
|
|
char buf[MAXMODNAME];
|
2008-01-16 15:34:50 +03:00
|
|
|
const char *s, *p;
|
|
|
|
int error;
|
2009-11-18 20:40:45 +03:00
|
|
|
size_t len;
|
2008-01-16 15:34:50 +03:00
|
|
|
|
|
|
|
KASSERT(mutex_owned(&module_lock));
|
|
|
|
|
2009-06-07 13:47:31 +04:00
|
|
|
filedict = NULL;
|
2008-01-16 15:34:50 +03:00
|
|
|
error = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Avoid recursing too far.
|
|
|
|
*/
|
|
|
|
if (++depth > maxdepth) {
|
|
|
|
module_error("too many required modules");
|
|
|
|
depth--;
|
|
|
|
return EMLINK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Load the module and link. Before going to the file system,
|
|
|
|
* scan the list of modules loaded by the boot loader. Just
|
|
|
|
* before init is started the list of modules loaded at boot
|
|
|
|
* will be purged. Before init is started we can assume that
|
2008-11-18 14:39:41 +03:00
|
|
|
* `name' is a module name and not a path name.
|
2008-01-16 15:34:50 +03:00
|
|
|
*/
|
|
|
|
TAILQ_FOREACH(mod, &module_bootlist, mod_chain) {
|
2008-11-18 14:39:41 +03:00
|
|
|
if (strcmp(mod->mod_info->mi_name, name) == 0) {
|
2008-01-16 15:34:50 +03:00
|
|
|
TAILQ_REMOVE(&module_bootlist, mod, mod_chain);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-05-05 01:35:12 +04:00
|
|
|
if (mod != NULL) {
|
|
|
|
TAILQ_INSERT_TAIL(&pending, mod, mod_chain);
|
|
|
|
} else {
|
2008-11-18 14:39:41 +03:00
|
|
|
/*
|
|
|
|
* If a requisite module, check to see if it is
|
|
|
|
* already present.
|
|
|
|
*/
|
|
|
|
if (isdep) {
|
|
|
|
TAILQ_FOREACH(mod, &module_list, mod_chain) {
|
|
|
|
if (strcmp(mod->mod_info->mi_name, name) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (mod != NULL) {
|
|
|
|
if (modp != NULL) {
|
|
|
|
*modp = mod;
|
|
|
|
}
|
|
|
|
depth--;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2008-01-16 15:34:50 +03:00
|
|
|
mod = kmem_zalloc(sizeof(*mod), KM_SLEEP);
|
|
|
|
if (mod == NULL) {
|
2008-12-28 06:21:02 +03:00
|
|
|
module_error("out of memory for `%s'", name);
|
2008-01-16 15:34:50 +03:00
|
|
|
depth--;
|
|
|
|
return ENOMEM;
|
|
|
|
}
|
2009-11-18 20:40:45 +03:00
|
|
|
|
|
|
|
error = module_load_vfs(name, flags, autoload, mod, &filedict);
|
2008-01-16 15:34:50 +03:00
|
|
|
if (error != 0) {
|
2008-01-19 21:20:39 +03:00
|
|
|
kmem_free(mod, sizeof(*mod));
|
|
|
|
depth--;
|
|
|
|
return error;
|
2008-01-19 03:57:35 +03:00
|
|
|
}
|
2008-01-16 15:34:50 +03:00
|
|
|
mod->mod_source = MODULE_SOURCE_FILESYS;
|
2009-11-18 20:40:45 +03:00
|
|
|
TAILQ_INSERT_TAIL(&pending, mod, mod_chain);
|
|
|
|
|
2008-05-01 18:44:48 +04:00
|
|
|
error = module_fetch_info(mod);
|
|
|
|
if (error != 0) {
|
2008-12-03 14:23:15 +03:00
|
|
|
module_error("cannot fetch module info for `%s'",
|
|
|
|
name);
|
2008-05-01 18:44:48 +04:00
|
|
|
goto fail;
|
|
|
|
}
|
2008-01-16 15:34:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2008-05-01 18:44:48 +04:00
|
|
|
* Check compatibility.
|
2008-01-16 15:34:50 +03:00
|
|
|
*/
|
|
|
|
mi = mod->mod_info;
|
2008-01-18 01:35:53 +03:00
|
|
|
if (strlen(mi->mi_name) >= MAXMODNAME) {
|
|
|
|
error = EINVAL;
|
2008-11-28 00:36:51 +03:00
|
|
|
module_error("module name `%s' too long", mi->mi_name);
|
2008-01-18 01:35:53 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
2008-06-01 00:14:38 +04:00
|
|
|
if (!module_compatible(mi->mi_version, __NetBSD_Version__)) {
|
2008-12-03 14:23:15 +03:00
|
|
|
module_error("module built for `%d', system `%d'",
|
2008-11-28 00:36:51 +03:00
|
|
|
mi->mi_version, __NetBSD_Version__);
|
2008-10-22 15:19:15 +04:00
|
|
|
if ((flags & MODCTL_LOAD_FORCE) != 0) {
|
|
|
|
module_error("forced load, system may be unstable");
|
|
|
|
} else {
|
|
|
|
error = EPROGMISMATCH;
|
|
|
|
goto fail;
|
|
|
|
}
|
2008-06-01 00:14:38 +04:00
|
|
|
}
|
2008-01-18 01:35:53 +03:00
|
|
|
|
2008-05-20 21:24:56 +04:00
|
|
|
/*
|
|
|
|
* If a specific kind of module was requested, ensure that we have
|
|
|
|
* a match.
|
|
|
|
*/
|
|
|
|
if (class != MODULE_CLASS_ANY && class != mi->mi_class) {
|
2008-12-03 15:14:11 +03:00
|
|
|
module_print("incompatible module class for `%s' (%d != %d)",
|
2008-11-28 00:36:51 +03:00
|
|
|
name, class, mi->mi_class);
|
2008-05-20 21:24:56 +04:00
|
|
|
error = ENOENT;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2008-01-16 15:34:50 +03:00
|
|
|
/*
|
2008-11-18 14:39:41 +03:00
|
|
|
* If loading a dependency, `name' is a plain module name.
|
2008-01-16 15:34:50 +03:00
|
|
|
* The name must match.
|
|
|
|
*/
|
2008-11-18 14:39:41 +03:00
|
|
|
if (isdep && strcmp(mi->mi_name, name) != 0) {
|
2008-11-28 00:36:51 +03:00
|
|
|
module_error("dependency name mismatch (`%s' != `%s')",
|
|
|
|
name, mi->mi_name);
|
2008-01-16 15:34:50 +03:00
|
|
|
error = ENOENT;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2008-01-18 01:35:53 +03:00
|
|
|
* Check to see if the module is already loaded. If so, we may
|
|
|
|
* have been recursively called to handle a dependency, so be sure
|
|
|
|
* to set modp.
|
2008-01-16 15:34:50 +03:00
|
|
|
*/
|
2008-01-18 01:35:53 +03:00
|
|
|
if ((mod2 = module_lookup(mi->mi_name)) != NULL) {
|
2008-01-18 17:29:44 +03:00
|
|
|
if (modp != NULL)
|
|
|
|
*modp = mod2;
|
2008-12-03 15:14:11 +03:00
|
|
|
module_print("module `%s' already loaded", mi->mi_name);
|
2008-01-16 15:34:50 +03:00
|
|
|
error = EEXIST;
|
|
|
|
goto fail;
|
|
|
|
}
|
2008-01-18 01:35:53 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Block circular dependencies.
|
|
|
|
*/
|
2008-01-16 15:34:50 +03:00
|
|
|
TAILQ_FOREACH(mod2, &pending, mod_chain) {
|
|
|
|
if (mod == mod2) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (strcmp(mod2->mod_info->mi_name, mi->mi_name) == 0) {
|
|
|
|
error = EDEADLK;
|
2008-11-28 00:36:51 +03:00
|
|
|
module_error("circular dependency detected for `%s'",
|
|
|
|
mi->mi_name);
|
2008-01-16 15:34:50 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now try to load any requisite modules.
|
|
|
|
*/
|
|
|
|
if (mi->mi_required != NULL) {
|
|
|
|
for (s = mi->mi_required; *s != '\0'; s = p) {
|
|
|
|
if (*s == ',')
|
|
|
|
s++;
|
|
|
|
p = s;
|
|
|
|
while (*p != '\0' && *p != ',')
|
|
|
|
p++;
|
2008-01-18 01:35:53 +03:00
|
|
|
len = p - s + 1;
|
|
|
|
if (len >= MAXMODNAME) {
|
|
|
|
error = EINVAL;
|
2008-11-28 00:36:51 +03:00
|
|
|
module_error("required module name `%s'"
|
|
|
|
" too long", mi->mi_required);
|
2008-01-18 01:35:53 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
2008-01-16 15:34:50 +03:00
|
|
|
strlcpy(buf, s, len);
|
|
|
|
if (buf[0] == '\0')
|
|
|
|
break;
|
|
|
|
if (mod->mod_nrequired == MAXMODDEPS - 1) {
|
|
|
|
error = EINVAL;
|
2008-11-28 00:36:51 +03:00
|
|
|
module_error("too many required modules (%d)",
|
|
|
|
mod->mod_nrequired);
|
2008-01-16 15:34:50 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
2008-01-18 19:41:46 +03:00
|
|
|
if (strcmp(buf, mi->mi_name) == 0) {
|
|
|
|
error = EDEADLK;
|
2008-11-28 00:36:51 +03:00
|
|
|
module_error("self-dependency detected for "
|
|
|
|
"`%s'", mi->mi_name);
|
2008-01-18 19:41:46 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
2008-03-02 14:18:43 +03:00
|
|
|
error = module_do_load(buf, true, flags, NULL,
|
2008-05-20 21:24:56 +04:00
|
|
|
&mod->mod_required[mod->mod_nrequired++],
|
2008-05-20 23:20:38 +04:00
|
|
|
MODULE_CLASS_ANY, true);
|
2008-11-18 14:39:41 +03:00
|
|
|
if (error != 0)
|
2008-01-16 15:34:50 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2008-05-20 17:34:44 +04:00
|
|
|
* We loaded all needed modules successfully: perform global
|
|
|
|
* relocations and initialize.
|
2008-01-16 15:34:50 +03:00
|
|
|
*/
|
2008-05-20 17:34:44 +04:00
|
|
|
error = kobj_affix(mod->mod_kobj, mi->mi_name);
|
|
|
|
if (error != 0) {
|
2008-12-05 15:51:17 +03:00
|
|
|
/* Cannot touch 'mi' as the module is now gone. */
|
|
|
|
module_error("unable to affix module `%s'", name);
|
2008-05-20 17:34:44 +04:00
|
|
|
goto fail2;
|
|
|
|
}
|
|
|
|
|
2009-11-18 20:40:45 +03:00
|
|
|
if (filedict) {
|
|
|
|
if (!module_merge_dicts(filedict, props)) {
|
|
|
|
module_error("module properties failed");
|
|
|
|
error = EINVAL;
|
2009-06-07 13:47:31 +04:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
2008-05-01 21:23:16 +04:00
|
|
|
KASSERT(module_active == NULL);
|
|
|
|
module_active = mod;
|
2009-11-18 20:40:45 +03:00
|
|
|
error = (*mi->mi_modcmd)(MODULE_CMD_INIT, filedict ? filedict : props);
|
2008-05-01 21:23:16 +04:00
|
|
|
module_active = NULL;
|
2009-11-18 20:40:45 +03:00
|
|
|
if (filedict) {
|
2009-06-07 13:47:31 +04:00
|
|
|
prop_object_release(filedict);
|
2009-11-18 20:40:45 +03:00
|
|
|
filedict = NULL;
|
2009-06-07 13:47:31 +04:00
|
|
|
}
|
2008-01-16 15:34:50 +03:00
|
|
|
if (error != 0) {
|
2008-11-28 00:36:51 +03:00
|
|
|
module_error("modcmd function returned error %d for `%s'",
|
|
|
|
error, mi->mi_name);
|
2008-01-16 15:34:50 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
2009-11-18 20:40:45 +03:00
|
|
|
|
First part of secmodel cleanup and other misc. changes:
- Separate the suser part of the bsd44 secmodel into its own secmodel
and directory, pending even more cleanups. For revision history
purposes, the original location of the files was
src/sys/secmodel/bsd44/secmodel_bsd44_suser.c
src/sys/secmodel/bsd44/suser.h
- Add a man-page for secmodel_suser(9) and update the one for
secmodel_bsd44(9).
- Add a "secmodel" module class and use it. Userland program and
documentation updated.
- Manage secmodel count (nsecmodels) through the module framework.
This eliminates the need for secmodel_{,de}register() calls in
secmodel code.
- Prepare for secmodel modularization by adding relevant module bits.
The secmodels don't allow auto unload. The bsd44 secmodel depends
on the suser and securelevel secmodels. The overlay secmodel depends
on the bsd44 secmodel. As the module class is only cosmetic, and to
prevent ambiguity, the bsd44 and overlay secmodels are prefixed with
"secmodel_".
- Adapt the overlay secmodel to recent changes (mainly vnode scope).
- Stop using link-sets for the sysctl node(s) creation.
- Keep sysctl variables under nodes of their relevant secmodels. In
other words, don't create duplicates for the suser/securelevel
secmodels under the bsd44 secmodel, as the latter is merely used
for "grouping".
- For the suser and securelevel secmodels, "advertise presence" in
relevant sysctl nodes (sysctl.security.models.{suser,securelevel}).
- Get rid of the LKM preprocessor stuff.
- As secmodels are now modules, there's no need for an explicit call
to secmodel_start(); it's handled by the module framework. That
said, the module framework was adjusted to properly load secmodels
early during system startup.
- Adapt rump to changes: Instead of using empty stubs for securelevel,
simply use the suser secmodel. Also replace secmodel_start() with a
call to secmodel_suser_start().
- 5.99.20.
Testing was done on i386 ("release" build). Spearated module_init()
changes were tested on sparc and sparc64 as well by martin@ (thanks!).
Mailing list reference:
http://mail-index.netbsd.org/tech-kern/2009/09/25/msg006135.html
2009-10-02 22:50:12 +04:00
|
|
|
if (mi->mi_class == MODULE_CLASS_SECMODEL)
|
|
|
|
secmodel_register();
|
2008-01-16 15:34:50 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Good, the module loaded successfully. Put it onto the
|
|
|
|
* list and add references to its requisite modules.
|
|
|
|
*/
|
|
|
|
TAILQ_REMOVE(&pending, mod, mod_chain);
|
2008-11-18 14:39:41 +03:00
|
|
|
module_enqueue(mod);
|
2008-01-16 15:34:50 +03:00
|
|
|
if (modp != NULL) {
|
|
|
|
*modp = mod;
|
|
|
|
}
|
2008-11-15 02:06:45 +03:00
|
|
|
if (autoload) {
|
|
|
|
/*
|
|
|
|
* Arrange to try unloading the module after
|
|
|
|
* a short delay.
|
|
|
|
*/
|
|
|
|
mod->mod_autotime = time_second + module_autotime;
|
|
|
|
module_thread_kick();
|
|
|
|
}
|
2008-01-16 15:34:50 +03:00
|
|
|
depth--;
|
|
|
|
return 0;
|
2008-01-19 03:57:35 +03:00
|
|
|
|
2008-01-16 15:34:50 +03:00
|
|
|
fail:
|
|
|
|
kobj_unload(mod->mod_kobj);
|
2008-05-20 17:34:44 +04:00
|
|
|
fail2:
|
2009-11-18 20:40:45 +03:00
|
|
|
if (filedict != NULL) {
|
|
|
|
prop_object_release(filedict);
|
|
|
|
filedict = NULL;
|
|
|
|
}
|
2008-05-20 17:34:44 +04:00
|
|
|
TAILQ_REMOVE(&pending, mod, mod_chain);
|
2008-01-16 15:34:50 +03:00
|
|
|
kmem_free(mod, sizeof(*mod));
|
|
|
|
depth--;
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* module_do_unload:
|
|
|
|
*
|
|
|
|
* Helper routine: do the dirty work of unloading a module.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
module_do_unload(const char *name)
|
|
|
|
{
|
|
|
|
module_t *mod;
|
|
|
|
int error;
|
|
|
|
u_int i;
|
|
|
|
|
|
|
|
KASSERT(mutex_owned(&module_lock));
|
|
|
|
|
|
|
|
mod = module_lookup(name);
|
|
|
|
if (mod == NULL) {
|
2008-11-28 00:36:51 +03:00
|
|
|
module_error("module `%s' not found", name);
|
2008-01-16 15:34:50 +03:00
|
|
|
return ENOENT;
|
|
|
|
}
|
|
|
|
if (mod->mod_refcnt != 0 || mod->mod_source == MODULE_SOURCE_KERNEL) {
|
2008-12-03 15:14:11 +03:00
|
|
|
module_print("module `%s' busy", name);
|
2008-01-16 15:34:50 +03:00
|
|
|
return EBUSY;
|
|
|
|
}
|
2008-05-01 21:23:16 +04:00
|
|
|
KASSERT(module_active == NULL);
|
|
|
|
module_active = mod;
|
2008-01-16 15:34:50 +03:00
|
|
|
error = (*mod->mod_info->mi_modcmd)(MODULE_CMD_FINI, NULL);
|
2008-05-01 21:23:16 +04:00
|
|
|
module_active = NULL;
|
2008-01-16 15:34:50 +03:00
|
|
|
if (error != 0) {
|
2008-12-03 15:14:11 +03:00
|
|
|
module_print("cannot unload module `%s' error=%d", name,
|
2008-12-03 14:23:15 +03:00
|
|
|
error);
|
2008-01-16 15:34:50 +03:00
|
|
|
return error;
|
|
|
|
}
|
First part of secmodel cleanup and other misc. changes:
- Separate the suser part of the bsd44 secmodel into its own secmodel
and directory, pending even more cleanups. For revision history
purposes, the original location of the files was
src/sys/secmodel/bsd44/secmodel_bsd44_suser.c
src/sys/secmodel/bsd44/suser.h
- Add a man-page for secmodel_suser(9) and update the one for
secmodel_bsd44(9).
- Add a "secmodel" module class and use it. Userland program and
documentation updated.
- Manage secmodel count (nsecmodels) through the module framework.
This eliminates the need for secmodel_{,de}register() calls in
secmodel code.
- Prepare for secmodel modularization by adding relevant module bits.
The secmodels don't allow auto unload. The bsd44 secmodel depends
on the suser and securelevel secmodels. The overlay secmodel depends
on the bsd44 secmodel. As the module class is only cosmetic, and to
prevent ambiguity, the bsd44 and overlay secmodels are prefixed with
"secmodel_".
- Adapt the overlay secmodel to recent changes (mainly vnode scope).
- Stop using link-sets for the sysctl node(s) creation.
- Keep sysctl variables under nodes of their relevant secmodels. In
other words, don't create duplicates for the suser/securelevel
secmodels under the bsd44 secmodel, as the latter is merely used
for "grouping".
- For the suser and securelevel secmodels, "advertise presence" in
relevant sysctl nodes (sysctl.security.models.{suser,securelevel}).
- Get rid of the LKM preprocessor stuff.
- As secmodels are now modules, there's no need for an explicit call
to secmodel_start(); it's handled by the module framework. That
said, the module framework was adjusted to properly load secmodels
early during system startup.
- Adapt rump to changes: Instead of using empty stubs for securelevel,
simply use the suser secmodel. Also replace secmodel_start() with a
call to secmodel_suser_start().
- 5.99.20.
Testing was done on i386 ("release" build). Spearated module_init()
changes were tested on sparc and sparc64 as well by martin@ (thanks!).
Mailing list reference:
http://mail-index.netbsd.org/tech-kern/2009/09/25/msg006135.html
2009-10-02 22:50:12 +04:00
|
|
|
if (mod->mod_info->mi_class == MODULE_CLASS_SECMODEL)
|
|
|
|
secmodel_deregister();
|
2008-01-16 15:34:50 +03:00
|
|
|
module_count--;
|
|
|
|
TAILQ_REMOVE(&module_list, mod, mod_chain);
|
|
|
|
for (i = 0; i < mod->mod_nrequired; i++) {
|
|
|
|
mod->mod_required[i]->mod_refcnt--;
|
|
|
|
}
|
|
|
|
if (mod->mod_kobj != NULL) {
|
|
|
|
kobj_unload(mod->mod_kobj);
|
|
|
|
}
|
|
|
|
kmem_free(mod, sizeof(*mod));
|
2008-11-15 02:06:45 +03:00
|
|
|
module_gen++;
|
2008-01-16 15:34:50 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* module_prime:
|
|
|
|
*
|
|
|
|
* Push a module loaded by the bootloader onto our internal
|
|
|
|
* list.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
module_prime(void *base, size_t size)
|
|
|
|
{
|
|
|
|
module_t *mod;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
mod = kmem_zalloc(sizeof(*mod), KM_SLEEP);
|
|
|
|
if (mod == NULL) {
|
|
|
|
return ENOMEM;
|
|
|
|
}
|
|
|
|
mod->mod_source = MODULE_SOURCE_BOOT;
|
|
|
|
|
2008-05-20 17:34:44 +04:00
|
|
|
error = kobj_load_mem(&mod->mod_kobj, base, size);
|
2008-01-16 15:34:50 +03:00
|
|
|
if (error != 0) {
|
2008-05-01 18:44:48 +04:00
|
|
|
kmem_free(mod, sizeof(*mod));
|
|
|
|
module_error("unable to load object pushed by boot loader");
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
error = module_fetch_info(mod);
|
|
|
|
if (error != 0) {
|
|
|
|
kobj_unload(mod->mod_kobj);
|
2008-01-16 15:34:50 +03:00
|
|
|
kmem_free(mod, sizeof(*mod));
|
2008-05-01 18:44:48 +04:00
|
|
|
module_error("unable to load object pushed by boot loader");
|
2008-01-16 15:34:50 +03:00
|
|
|
return error;
|
|
|
|
}
|
2008-05-01 18:44:48 +04:00
|
|
|
|
2008-01-16 15:34:50 +03:00
|
|
|
TAILQ_INSERT_TAIL(&module_bootlist, mod, mod_chain);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2008-05-01 18:44:48 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* module_fetch_into:
|
|
|
|
*
|
|
|
|
* Fetch modinfo record from a loaded module.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
module_fetch_info(module_t *mod)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
void *addr;
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find module info record and check compatibility.
|
|
|
|
*/
|
|
|
|
error = kobj_find_section(mod->mod_kobj, "link_set_modules",
|
|
|
|
&addr, &size);
|
|
|
|
if (error != 0) {
|
|
|
|
module_error("`link_set_modules' section not present");
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
if (size != sizeof(modinfo_t **)) {
|
|
|
|
module_error("`link_set_modules' section wrong size");
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
mod->mod_info = *(modinfo_t **)addr;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2008-05-01 21:23:16 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* module_find_section:
|
|
|
|
*
|
|
|
|
* Allows a module that is being initialized to look up a section
|
|
|
|
* within its ELF object.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
module_find_section(const char *name, void **addr, size_t *size)
|
|
|
|
{
|
|
|
|
|
|
|
|
KASSERT(mutex_owned(&module_lock));
|
|
|
|
KASSERT(module_active != NULL);
|
|
|
|
|
|
|
|
return kobj_find_section(module_active->mod_kobj, name, addr, size);
|
|
|
|
}
|
2008-11-15 02:06:45 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* module_thread:
|
|
|
|
*
|
|
|
|
* Automatically unload modules. We try once to unload autoloaded
|
|
|
|
* modules after module_autotime seconds. If the system is under
|
|
|
|
* severe memory pressure, we'll try unloading all modules.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
module_thread(void *cookie)
|
|
|
|
{
|
|
|
|
module_t *mod, *next;
|
2008-11-18 14:56:09 +03:00
|
|
|
modinfo_t *mi;
|
|
|
|
int error;
|
2008-11-15 02:06:45 +03:00
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
mutex_enter(&module_lock);
|
|
|
|
for (mod = TAILQ_FIRST(&module_list); mod != NULL; mod = next) {
|
|
|
|
next = TAILQ_NEXT(mod, mod_chain);
|
2008-12-05 15:55:09 +03:00
|
|
|
if (uvmexp.free < uvmexp.freemin) {
|
2008-11-15 02:06:45 +03:00
|
|
|
module_thread_ticks = hz;
|
|
|
|
} else if (mod->mod_autotime == 0) {
|
|
|
|
continue;
|
|
|
|
} else if (time_second < mod->mod_autotime) {
|
|
|
|
module_thread_ticks = hz;
|
|
|
|
continue;
|
2008-12-05 15:55:09 +03:00
|
|
|
} else {
|
2008-11-15 02:06:45 +03:00
|
|
|
mod->mod_autotime = 0;
|
2008-12-05 15:55:09 +03:00
|
|
|
}
|
2008-11-18 14:56:09 +03:00
|
|
|
/*
|
|
|
|
* If this module wants to avoid autounload then
|
|
|
|
* skip it. Some modules can ping-pong in and out
|
|
|
|
* because their use is transient but often.
|
|
|
|
* Example: exec_script.
|
|
|
|
*/
|
|
|
|
mi = mod->mod_info;
|
|
|
|
error = (*mi->mi_modcmd)(MODULE_CMD_AUTOUNLOAD, NULL);
|
|
|
|
if (error == 0 || error == ENOTTY) {
|
2008-12-05 15:55:09 +03:00
|
|
|
(void)module_do_unload(mi->mi_name);
|
2008-11-18 14:56:09 +03:00
|
|
|
}
|
2008-11-15 02:06:45 +03:00
|
|
|
}
|
|
|
|
mutex_exit(&module_lock);
|
|
|
|
|
|
|
|
mutex_enter(&module_thread_lock);
|
|
|
|
(void)cv_timedwait(&module_thread_cv, &module_thread_lock,
|
|
|
|
module_thread_ticks);
|
|
|
|
module_thread_ticks = 0;
|
|
|
|
mutex_exit(&module_thread_lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* module_thread:
|
|
|
|
*
|
|
|
|
* Kick the module thread into action, perhaps because the
|
|
|
|
* system is low on memory.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
module_thread_kick(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
mutex_enter(&module_thread_lock);
|
|
|
|
module_thread_ticks = hz;
|
|
|
|
cv_broadcast(&module_thread_cv);
|
|
|
|
mutex_exit(&module_thread_lock);
|
|
|
|
}
|
2008-11-25 18:14:07 +03:00
|
|
|
|
|
|
|
#ifdef DDB
|
|
|
|
/*
|
|
|
|
* module_whatis:
|
|
|
|
*
|
|
|
|
* Helper routine for DDB.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
module_whatis(uintptr_t addr, void (*pr)(const char *, ...))
|
|
|
|
{
|
|
|
|
module_t *mod;
|
|
|
|
size_t msize;
|
|
|
|
vaddr_t maddr;
|
|
|
|
|
|
|
|
TAILQ_FOREACH(mod, &module_list, mod_chain) {
|
2009-05-24 18:54:17 +04:00
|
|
|
if (mod->mod_kobj == NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
2009-06-18 01:04:25 +04:00
|
|
|
if (kobj_stat(mod->mod_kobj, &maddr, &msize) != 0)
|
|
|
|
continue;
|
2008-11-25 18:14:07 +03:00
|
|
|
if (addr < maddr || addr >= maddr + msize) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
(*pr)("%p is %p+%zu, in kernel module `%s'\n",
|
|
|
|
(void *)addr, (void *)maddr,
|
|
|
|
(size_t)(addr - maddr), mod->mod_info->mi_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* module_print_list:
|
|
|
|
*
|
|
|
|
* Helper routine for DDB.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
module_print_list(void (*pr)(const char *, ...))
|
|
|
|
{
|
|
|
|
const char *src;
|
|
|
|
module_t *mod;
|
|
|
|
size_t msize;
|
|
|
|
vaddr_t maddr;
|
|
|
|
|
|
|
|
(*pr)("%16s %16s %8s %8s\n", "NAME", "TEXT/DATA", "SIZE", "SOURCE");
|
|
|
|
|
|
|
|
TAILQ_FOREACH(mod, &module_list, mod_chain) {
|
|
|
|
switch (mod->mod_source) {
|
|
|
|
case MODULE_SOURCE_KERNEL:
|
|
|
|
src = "builtin";
|
|
|
|
break;
|
|
|
|
case MODULE_SOURCE_FILESYS:
|
|
|
|
src = "filesys";
|
|
|
|
break;
|
|
|
|
case MODULE_SOURCE_BOOT:
|
|
|
|
src = "boot";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
src = "unknown";
|
|
|
|
break;
|
|
|
|
}
|
2009-06-18 01:04:25 +04:00
|
|
|
if (mod->mod_kobj == NULL) {
|
2009-05-24 18:54:17 +04:00
|
|
|
maddr = 0;
|
|
|
|
msize = 0;
|
2009-06-18 01:04:25 +04:00
|
|
|
} else if (kobj_stat(mod->mod_kobj, &maddr, &msize) != 0)
|
|
|
|
continue;
|
2008-11-25 18:15:28 +03:00
|
|
|
(*pr)("%16s %16lx %8ld %8s\n", mod->mod_info->mi_name,
|
2008-11-25 18:14:07 +03:00
|
|
|
(long)maddr, (long)msize, src);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* DDB */
|
2009-06-07 13:47:31 +04:00
|
|
|
|
2009-06-09 23:09:03 +04:00
|
|
|
static bool
|
|
|
|
module_merge_dicts(prop_dictionary_t existing_dict,
|
|
|
|
const prop_dictionary_t new_dict)
|
|
|
|
{
|
|
|
|
prop_dictionary_keysym_t props_keysym;
|
|
|
|
prop_object_iterator_t props_iter;
|
|
|
|
prop_object_t props_obj;
|
|
|
|
const char *props_key;
|
|
|
|
bool error;
|
|
|
|
|
2009-10-16 04:27:07 +04:00
|
|
|
if (new_dict == NULL) { /* nothing to merge */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-06-09 23:09:03 +04:00
|
|
|
error = false;
|
|
|
|
props_iter = prop_dictionary_iterator(new_dict);
|
|
|
|
if (props_iter == NULL) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((props_obj = prop_object_iterator_next(props_iter)) != NULL) {
|
|
|
|
props_keysym = (prop_dictionary_keysym_t)props_obj;
|
|
|
|
props_key = prop_dictionary_keysym_cstring_nocopy(props_keysym);
|
|
|
|
props_obj = prop_dictionary_get_keysym(new_dict, props_keysym);
|
|
|
|
if ((props_obj == NULL) || !prop_dictionary_set(existing_dict,
|
|
|
|
props_key, props_obj)) {
|
|
|
|
error = true;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
error = false;
|
|
|
|
|
|
|
|
out:
|
|
|
|
prop_object_iterator_release(props_iter);
|
|
|
|
|
|
|
|
return !error;
|
|
|
|
}
|