/*-
* Copyright (c) 2008 The NetBSD Foundation, Inc.
* 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.
*
* 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.
*/
/*
* System calls relating to loadable modules.
*/
/*
* Count up the number of modstat_t needed, and total size of
* require_module lists on both active and built-in lists
*/
TAILQ_FOREACH(mod, &module_list, mod_chain) {
ms_cnt++;
mi = mod->mod_info;
if (mi->mi_required != NULL) {
req_len += strlen(mi->mi_required) + 1;
}
}
TAILQ_FOREACH(mod, &module_builtins, mod_chain) {
ms_cnt++;
mi = mod->mod_info;
if (mi->mi_required != NULL) {
req_len += strlen(mi->mi_required) + 1;
}
}
/* Allocate internal buffers to hold all the output data */
ms_len = ms_cnt * sizeof(modstat_t);
ms = kmem_zalloc(ms_len, KM_SLEEP);
req = kmem_zalloc(req_len, KM_SLEEP);
mso = ms;
reqo = req++;
off = 1;
/*
* Load data into our internal buffers for both active and
* built-in module lists
*/
TAILQ_FOREACH(mod, &module_list, mod_chain) {
mi = mod->mod_info;
strlcpy(ms->ms_name, mi->mi_name, sizeof(ms->ms_name));
if (mi->mi_required != NULL) {
ms->ms_reqoffset = off;
used = strlcpy(req, mi->mi_required, req_len - off);
KASSERTMSG(used < req_len - off, "reqlist grew!");
off += used + 1;
req += used + 1;
} else
ms->ms_reqoffset = 0;
if (mod->mod_kobj != NULL && stataddr) {
kobj_stat(mod->mod_kobj, &addr, &size);
ms->ms_addr = addr;
ms->ms_size = size;
}
ms->ms_class = mi->mi_class;
ms->ms_refcnt = mod->mod_refcnt;
ms->ms_source = mod->mod_source;
ms->ms_flags = mod->mod_flags;
ms++;
}
TAILQ_FOREACH(mod, &module_builtins, mod_chain) {
mi = mod->mod_info;
strlcpy(ms->ms_name, mi->mi_name, sizeof(ms->ms_name));
if (mi->mi_required != NULL) {
ms->ms_reqoffset = off;
used = strlcpy(req, mi->mi_required, req_len - off);
KASSERTMSG(used < req_len - off, "reqlist grew!");
off += used + 1;
req += used + 1;
} else
ms->ms_reqoffset = 0;
if (mod->mod_kobj != NULL && stataddr) {
kobj_stat(mod->mod_kobj, &addr, &size);
ms->ms_addr = addr;
ms->ms_size = size;
}
ms->ms_class = mi->mi_class;
ms->ms_refcnt = -1;
KASSERT(mod->mod_source == MODULE_SOURCE_KERNEL);
ms->ms_source = mod->mod_source;
ms++;
}
kernconfig_unlock();
/*
* Now copyout our internal buffers back to userland
*/
out_p = iov->iov_base;
out_s = iov->iov_len;
size = sizeof(ms_cnt);
/* Copy out the count of modstat_t */
if (out_s) {
size = uimin(sizeof(ms_cnt), out_s);
error = copyout(&ms_cnt, out_p, size);
out_p += size;
out_s -= size;
}
/* Copy out the modstat_t array */
if (out_s && error == 0) {
size = uimin(ms_len, out_s);
error = copyout(mso, out_p, size);
out_p += size;
out_s -= size;
}
/* Copy out the "required" strings */
if (out_s && error == 0) {
size = uimin(req_len, out_s);
error = copyout(reqo, out_p, size);
out_p += size;
out_s -= size;
}
kmem_free(mso, ms_len);
kmem_free(reqo, req_len);
/* Finally, update the userland copy of the iovec's length */
if (error == 0) {
iov->iov_len = ms_len + req_len + sizeof(ms_cnt);
error = copyout(iov, arg, sizeof(*iov));
}