#include <sys/cdefs.h>
#include <stand.h>
#include <string.h>
#include <sys/param.h>
#include <sys/linker.h>
#include <sys/module.h>
#include <sys/queue.h>
#include <sys/stdint.h>
#include <sys/tem_impl.h>
#include <sys/font.h>
#include <sys/sha1.h>
#include <libcrypto.h>
#include "bootstrap.h"
#if defined(EFI)
#define PTOV(pa) ((void *)pa)
#else
#include "../i386/btx/lib/btxv86.h"
#endif
#define MDIR_REMOVED 0x0001
#define MDIR_NOHINTS 0x0002
struct moduledir {
char *d_path;
uchar_t *d_hints;
int d_hintsz;
int d_flags;
STAILQ_ENTRY(moduledir) d_link;
};
static int file_load(char *, vm_offset_t, struct preloaded_file **);
static int file_load_dependencies(struct preloaded_file *);
static char *file_search(const char *, const char **);
static struct kernel_module *file_findmodule(struct preloaded_file *, char *,
struct mod_depend *);
static int file_havepath(const char *);
static char *mod_searchmodule(char *, struct mod_depend *);
static void file_insert_tail(struct preloaded_file *);
static void file_remove(struct preloaded_file *);
struct file_metadata *metadata_next(struct file_metadata *, int);
static void moduledir_readhints(struct moduledir *);
static void moduledir_rebuild(void);
static vm_offset_t loadaddr = 0;
#if defined(LOADER_FDT_SUPPORT)
static const char *default_searchpath = "/boot/kernel;/boot/modules;/boot/dtb";
#else
static const char *default_searchpath = "/platform/i86pc";
#endif
static STAILQ_HEAD(, moduledir) moduledir_list =
STAILQ_HEAD_INITIALIZER(moduledir_list);
struct preloaded_file *preloaded_files = NULL;
static const char *kld_ext_list[] = {
".ko",
"",
".debug",
NULL
};
COMMAND_SET(load, "load", "load a kernel or module", command_load);
static int
command_load(int argc, char *argv[])
{
char *typestr;
int dofile, dokld, ch, error;
dokld = dofile = 0;
optind = 1;
optreset = 1;
typestr = NULL;
if (argc == 1) {
command_errmsg = "no filename specified";
return (CMD_CRIT);
}
while ((ch = getopt(argc, argv, "kt:")) != -1) {
switch (ch) {
case 'k':
dokld = 1;
break;
case 't':
typestr = optarg;
dofile = 1;
break;
case '?':
default:
return (CMD_OK);
}
}
argv += (optind - 1);
argc -= (optind - 1);
printf("Loading %s...\n", argv[1]);
if (dofile) {
struct preloaded_file *fp;
if ((typestr == NULL) || (*typestr == 0)) {
command_errmsg = "invalid load type";
return (CMD_CRIT);
}
if (file_findfile(argv[1], typestr) != NULL) {
(void) snprintf(command_errbuf, sizeof (command_errbuf),
"warning: file '%s' already loaded", argv[1]);
return (CMD_WARN);
}
fp = file_loadraw(argv[1], typestr, argc - 2, argv + 2, 1);
if (fp != NULL)
return (CMD_OK);
if (strcmp("mfs_root", typestr) == 0)
return (CMD_FATAL);
return (CMD_ERROR);
}
if (dokld || file_havepath(argv[1])) {
error = mod_loadkld(argv[1], argc - 2, argv + 2);
if (error == EEXIST) {
(void) snprintf(command_errbuf, sizeof (command_errbuf),
"warning: KLD '%s' already loaded", argv[1]);
return (CMD_WARN);
}
return (error == 0 ? CMD_OK : CMD_CRIT);
}
error = mod_load(argv[1], NULL, argc - 2, argv + 2);
if (error == EEXIST) {
(void) snprintf(command_errbuf, sizeof (command_errbuf),
"warning: module '%s' already loaded", argv[1]);
return (CMD_WARN);
}
return (error == 0 ? CMD_OK : CMD_CRIT);
}
void
unload(void)
{
struct preloaded_file *fp;
while (preloaded_files != NULL) {
fp = preloaded_files;
preloaded_files = preloaded_files->f_next;
file_discard(fp);
}
loadaddr = 0;
(void) unsetenv("kernelname");
}
COMMAND_SET(unload, "unload", "unload all modules", command_unload);
static int
command_unload(int argc __unused, char *argv[] __unused)
{
unload();
return (CMD_OK);
}
COMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod);
static int
command_lsmod(int argc, char *argv[])
{
struct preloaded_file *fp;
struct kernel_module *mp;
struct file_metadata *md;
char lbuf[80];
int ch, verbose, hash, ret = 0;
verbose = 0;
hash = 0;
optind = 1;
optreset = 1;
while ((ch = getopt(argc, argv, "vs")) != -1) {
switch (ch) {
case 'v':
verbose = 1;
break;
case 's':
hash = 1;
break;
case '?':
default:
return (CMD_OK);
}
}
pager_open();
for (fp = preloaded_files; fp; fp = fp->f_next) {
(void) snprintf(lbuf, sizeof (lbuf), " %p: ",
(void *) fp->f_addr);
(void) pager_output(lbuf);
(void) pager_output(fp->f_name);
(void) snprintf(lbuf, sizeof (lbuf), " (%s, 0x%lx)\n",
fp->f_type, (long)fp->f_size);
if (pager_output(lbuf))
break;
if (fp->f_args != NULL) {
(void) pager_output(" args: ");
(void) pager_output(fp->f_args);
if (pager_output("\n"))
break;
if (strcmp(fp->f_type, "hash") == 0) {
size_t dsize;
(void) pager_output(" contents: ");
dsize = fp->f_size + 1;
if (sizeof (lbuf) < dsize)
dsize = sizeof (lbuf);
(void) strlcpy(lbuf, ptov(fp->f_addr), dsize);
if (pager_output(lbuf))
break;
}
}
if (hash == 1) {
void *ptr = PTOV(fp->f_addr);
(void) pager_output(" hash: ");
sha1(ptr, fp->f_size, (uint8_t *)lbuf);
for (int i = 0; i < SHA1_DIGEST_LENGTH; i++)
printf("%02x", (int)(lbuf[i] & 0xff));
if (pager_output("\n"))
break;
}
if (fp->f_modules) {
(void) pager_output(" modules: ");
for (mp = fp->f_modules; mp; mp = mp->m_next) {
(void) snprintf(lbuf, sizeof (lbuf), "%s.%d ",
mp->m_name, mp->m_version);
(void) pager_output(lbuf);
}
if (pager_output("\n"))
break;
}
if (verbose) {
for (md = fp->f_metadata; md != NULL;
md = md->md_next) {
(void) snprintf(lbuf, sizeof (lbuf),
" 0x%04x, 0x%lx\n",
md->md_type, (long)md->md_size);
if ((ret = pager_output(lbuf)))
break;
}
}
if (ret != 0)
break;
}
pager_close();
return (CMD_OK);
}
int
file_load(char *filename, vm_offset_t dest, struct preloaded_file **result)
{
static int last_file_format = 0;
struct preloaded_file *fp;
int error;
int i;
if (preloaded_files == NULL)
last_file_format = 0;
error = EFTYPE;
for (i = last_file_format, fp = NULL;
file_formats[i] && fp == NULL; i++) {
error = (file_formats[i]->l_load)(filename, dest, &fp);
if (error == 0) {
fp->f_loader = last_file_format = i;
*result = fp;
break;
} else if (last_file_format == i && i != 0) {
i = -1;
last_file_format = 0;
fp = NULL;
continue;
}
if (error == EFTYPE)
continue;
if (error) {
(void) snprintf(command_errbuf, sizeof (command_errbuf),
"can't load file '%s': %s", filename,
strerror(error));
break;
}
}
return (error);
}
static int
file_load_dependencies(struct preloaded_file *base_file)
{
struct file_metadata *md;
struct preloaded_file *fp;
struct mod_depend *verinfo;
struct kernel_module *mp;
char *dmodname;
int error;
md = file_findmetadata(base_file, MODINFOMD_DEPLIST);
if (md == NULL)
return (0);
error = 0;
do {
verinfo = (struct mod_depend *)md->md_data;
dmodname = (char *)(verinfo + 1);
if (file_findmodule(NULL, dmodname, verinfo) == NULL) {
printf("loading required module '%s'\n", dmodname);
error = mod_load(dmodname, verinfo, 0, NULL);
if (error)
break;
mp = file_findmodule(NULL, dmodname, verinfo);
if (mp == NULL) {
(void) snprintf(command_errbuf,
sizeof (command_errbuf),
"module '%s' exists but with wrong version",
dmodname);
error = ENOENT;
break;
}
}
md = metadata_next(md, MODINFOMD_DEPLIST);
} while (md);
if (!error)
return (0);
while (base_file != NULL) {
fp = base_file;
base_file = base_file->f_next;
file_discard(fp);
}
return (error);
}
static size_t
env_get_size(void)
{
size_t size = 0;
struct env_var *ep;
for (ep = environ; ep != NULL; ep = ep->ev_next) {
size += strlen(ep->ev_name);
size++;
if (ep->ev_value != NULL)
size += strlen(ep->ev_value);
size++;
}
size++;
return (size);
}
static void
module_hash(struct preloaded_file *fp, void *addr, size_t size)
{
uint8_t hash[SHA1_DIGEST_LENGTH];
char ascii[2 * SHA1_DIGEST_LENGTH + 1];
int i;
sha1(addr, size, hash);
for (i = 0; i < SHA1_DIGEST_LENGTH; i++) {
(void) snprintf(ascii + 2 * i, sizeof (ascii) - 2 * i, "%02x",
hash[i] & 0xff);
}
(void) asprintf(&fp->f_args, "hash=%s", ascii);
}
void
build_environment_module(void)
{
struct preloaded_file *fp;
size_t size;
char *name = "environment";
vm_offset_t laddr;
if (file_findfile(NULL, NULL) == NULL) {
printf("Can not load environment module: %s\n",
"the kernel is not loaded");
return;
}
if (file_findfile(name, name) != NULL) {
printf("warning: '%s' is already loaded\n", name);
return;
}
tem_save_state();
size = env_get_size();
fp = file_alloc();
if (fp != NULL) {
fp->f_name = strdup(name);
fp->f_type = strdup(name);
}
if (fp == NULL || fp->f_name == NULL || fp->f_type == NULL) {
printf("Can not load environment module: %s\n",
"out of memory");
file_discard(fp);
return;
}
if (archsw.arch_loadaddr != NULL)
loadaddr = archsw.arch_loadaddr(LOAD_MEM, &size, loadaddr);
if (loadaddr == 0) {
printf("Can not load environment module: %s\n",
"out of memory");
file_discard(fp);
return;
}
laddr = bi_copyenv(loadaddr);
module_hash(fp, PTOV(loadaddr), laddr - loadaddr);
fp->f_loader = -1;
fp->f_addr = loadaddr;
fp->f_size = laddr - loadaddr;
loadaddr = laddr;
file_insert_tail(fp);
}
void
build_font_module(void)
{
bitmap_data_t *bd;
struct font *fd;
struct preloaded_file *fp;
size_t size;
uint32_t checksum;
int i;
char *name = "console-font";
vm_offset_t laddr;
struct font_info fi;
struct fontlist *fl;
if (STAILQ_EMPTY(&fonts))
return;
if (file_findfile(NULL, NULL) == NULL) {
printf("Can not load font module: %s\n",
"the kernel is not loaded");
return;
}
if (file_findfile(name, name) != NULL) {
printf("warning: '%s' is already loaded\n", name);
return;
}
bd = NULL;
STAILQ_FOREACH(fl, &fonts, font_next) {
if (tems.ts_font.vf_width == fl->font_data->width &&
tems.ts_font.vf_height == fl->font_data->height) {
if (fl->font_flags == FONT_BUILTIN)
return;
bd = fl->font_data;
break;
}
}
if (bd == NULL)
return;
fd = bd->font;
fi.fi_width = fd->vf_width;
checksum = fi.fi_width;
fi.fi_height = fd->vf_height;
checksum += fi.fi_height;
fi.fi_bitmap_size = bd->uncompressed_size;
checksum += fi.fi_bitmap_size;
size = roundup2(sizeof (struct font_info), 8);
for (i = 0; i < VFNT_MAPS; i++) {
fi.fi_map_count[i] = fd->vf_map_count[i];
checksum += fi.fi_map_count[i];
size += fd->vf_map_count[i] * sizeof (struct font_map);
size += roundup2(size, 8);
}
size += bd->uncompressed_size;
fi.fi_checksum = -checksum;
fp = file_alloc();
if (fp != NULL) {
fp->f_name = strdup(name);
fp->f_type = strdup(name);
}
if (fp == NULL || fp->f_name == NULL || fp->f_type == NULL) {
printf("Can not load font module: %s\n",
"out of memory");
file_discard(fp);
return;
}
if (archsw.arch_loadaddr != NULL)
loadaddr = archsw.arch_loadaddr(LOAD_MEM, &size, loadaddr);
if (loadaddr == 0) {
printf("Can not load font module: %s\n",
"out of memory");
file_discard(fp);
return;
}
laddr = loadaddr;
laddr += archsw.arch_copyin(&fi, laddr, sizeof (struct font_info));
laddr = roundup2(laddr, 8);
for (i = 0; i < VFNT_MAPS; i++) {
if (fd->vf_map_count[i] != 0) {
laddr += archsw.arch_copyin(fd->vf_map[i], laddr,
fd->vf_map_count[i] * sizeof (struct font_map));
laddr = roundup2(laddr, 8);
}
}
laddr += archsw.arch_copyin(fd->vf_bytes, laddr, fi.fi_bitmap_size);
module_hash(fp, PTOV(loadaddr), laddr - loadaddr);
fp->f_loader = -1;
fp->f_addr = loadaddr;
fp->f_size = laddr - loadaddr;
loadaddr = laddr;
file_insert_tail(fp);
}
struct preloaded_file *
file_loadraw(const char *fname, char *type, int argc, char **argv, int insert)
{
struct preloaded_file *fp;
char *name;
int fd;
ssize_t got;
struct stat st;
if ((file_findfile(NULL, NULL)) == NULL) {
command_errmsg = "can't load file before kernel";
return (NULL);
}
name = file_search(fname, NULL);
if (name == NULL) {
(void) snprintf(command_errbuf, sizeof (command_errbuf),
"can't find '%s'", fname);
return (NULL);
}
if ((fd = open(name, O_RDONLY)) < 0) {
(void) snprintf(command_errbuf, sizeof (command_errbuf),
"can't open '%s': %s", name, strerror(errno));
free(name);
return (NULL);
}
if (fstat(fd, &st) < 0) {
(void) close(fd);
(void) snprintf(command_errbuf, sizeof (command_errbuf),
"stat error '%s': %s", name, strerror(errno));
free(name);
return (NULL);
}
if (archsw.arch_loadaddr != NULL)
loadaddr = archsw.arch_loadaddr(LOAD_RAW, name, loadaddr);
if (loadaddr == 0) {
(void) close(fd);
(void) snprintf(command_errbuf, sizeof (command_errbuf),
"no memory to load %s", name);
free(name);
return (NULL);
}
got = archsw.arch_readin(fd, loadaddr, st.st_size);
if ((size_t)got != st.st_size) {
(void) snprintf(command_errbuf, sizeof (command_errbuf),
"error reading '%s': %s", name, strerror(errno));
free(name);
(void) close(fd);
if (archsw.arch_free_loadaddr != NULL && st.st_size != 0) {
archsw.arch_free_loadaddr(loadaddr,
(uint64_t)(roundup2(st.st_size, PAGE_SIZE) >> 12));
}
return (NULL);
}
fp = file_alloc();
if (fp == NULL) {
if (archsw.arch_free_loadaddr != NULL && st.st_size != 0)
archsw.arch_free_loadaddr(loadaddr,
(uint64_t)(roundup2(st.st_size, PAGE_SIZE) >> 12));
(void) snprintf(command_errbuf, sizeof (command_errbuf),
"no memory to load %s", name);
free(name);
(void) close(fd);
return (NULL);
}
fp->f_name = name;
fp->f_args = unargv(argc, argv);
fp->f_type = strdup(type);
fp->f_metadata = NULL;
fp->f_loader = -1;
fp->f_addr = loadaddr;
fp->f_size = st.st_size;
if (fp->f_type == NULL ||
(argc != 0 && fp->f_args == NULL)) {
(void) close(fd);
(void) snprintf(command_errbuf, sizeof (command_errbuf),
"no memory to load %s", name);
file_discard(fp);
return (NULL);
}
loadaddr += st.st_size;
if (insert != 0)
file_insert_tail(fp);
(void) close(fd);
return (fp);
}
int
mod_load(char *modname, struct mod_depend *verinfo, int argc, char *argv[])
{
struct kernel_module *mp;
int err;
char *filename;
if (file_havepath(modname)) {
printf("Warning: mod_load() called instead of mod_loadkld() "
"for module '%s'\n", modname);
return (mod_loadkld(modname, argc, argv));
}
mp = file_findmodule(NULL, modname, verinfo);
if (mp != NULL) {
free(mp->m_args);
mp->m_args = unargv(argc, argv);
(void) snprintf(command_errbuf, sizeof (command_errbuf),
"warning: module '%s' already loaded", mp->m_name);
return (0);
}
filename = mod_searchmodule(modname, verinfo);
if (filename == NULL) {
(void) snprintf(command_errbuf, sizeof (command_errbuf),
"can't find '%s'", modname);
return (ENOENT);
}
err = mod_loadkld(filename, argc, argv);
free(filename);
return (err);
}
int
mod_loadkld(const char *kldname, int argc, char *argv[])
{
struct preloaded_file *fp;
int err;
char *filename;
vm_offset_t loadaddr_saved;
filename = file_search(kldname, kld_ext_list);
if (filename == NULL) {
(void) snprintf(command_errbuf, sizeof (command_errbuf),
"can't find '%s'", kldname);
return (ENOENT);
}
fp = file_findfile(filename, NULL);
if (fp != NULL) {
(void) snprintf(command_errbuf, sizeof (command_errbuf),
"warning: KLD '%s' already loaded", filename);
free(filename);
return (0);
}
do {
err = file_load(filename, loadaddr, &fp);
if (err)
break;
fp->f_args = unargv(argc, argv);
loadaddr_saved = loadaddr;
loadaddr = fp->f_addr + fp->f_size;
file_insert_tail(fp);
if (file_load_dependencies(fp) != 0) {
err = ENOENT;
file_remove(fp);
loadaddr = loadaddr_saved;
fp = NULL;
break;
}
} while (0);
if (err == EFTYPE) {
(void) snprintf(command_errbuf, sizeof (command_errbuf),
"don't know how to load module '%s'", filename);
}
if (err)
file_discard(fp);
free(filename);
return (err);
}
struct preloaded_file *
file_findfile(const char *name, const char *type)
{
struct preloaded_file *fp;
for (fp = preloaded_files; fp != NULL; fp = fp->f_next) {
if (((name == NULL) || strcmp(name, fp->f_name) == 0) &&
((type == NULL) || strcmp(type, fp->f_type) == 0))
break;
}
return (fp);
}
struct kernel_module *
file_findmodule(struct preloaded_file *fp, char *modname,
struct mod_depend *verinfo)
{
struct kernel_module *mp, *best;
int bestver, mver;
if (fp == NULL) {
for (fp = preloaded_files; fp; fp = fp->f_next) {
mp = file_findmodule(fp, modname, verinfo);
if (mp != NULL)
return (mp);
}
return (NULL);
}
best = NULL;
bestver = 0;
for (mp = fp->f_modules; mp; mp = mp->m_next) {
if (strcmp(modname, mp->m_name) == 0) {
if (verinfo == NULL)
return (mp);
mver = mp->m_version;
if (mver == verinfo->md_ver_preferred)
return (mp);
if (mver >= verinfo->md_ver_minimum &&
mver <= verinfo->md_ver_maximum &&
mver > bestver) {
best = mp;
bestver = mver;
}
}
}
return (best);
}
void
file_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p)
{
struct file_metadata *md;
md = malloc(sizeof (struct file_metadata) - sizeof (md->md_data) +
size);
if (md != NULL) {
md->md_size = size;
md->md_type = type;
bcopy(p, md->md_data, size);
md->md_next = fp->f_metadata;
}
fp->f_metadata = md;
}
struct file_metadata *
file_findmetadata(struct preloaded_file *fp, int type)
{
struct file_metadata *md;
for (md = fp->f_metadata; md != NULL; md = md->md_next)
if (md->md_type == type)
break;
return (md);
}
struct file_metadata *
metadata_next(struct file_metadata *md, int type)
{
if (md == NULL)
return (NULL);
while ((md = md->md_next) != NULL)
if (md->md_type == type)
break;
return (md);
}
static const char *emptyextlist[] = { "", NULL };
static char *
file_lookup(const char *path, const char *name, int namelen,
const char **extlist)
{
struct stat st;
char *result, *cp;
const char **cpp;
int pathlen, extlen, len;
pathlen = strlen(path);
extlen = 0;
if (extlist == NULL)
extlist = emptyextlist;
for (cpp = extlist; *cpp; cpp++) {
len = strlen(*cpp);
if (len > extlen)
extlen = len;
}
result = malloc(pathlen + namelen + extlen + 2);
if (result == NULL)
return (NULL);
bcopy(path, result, pathlen);
if (pathlen > 0 && result[pathlen - 1] != '/')
result[pathlen++] = '/';
cp = result + pathlen;
bcopy(name, cp, namelen);
cp += namelen;
for (cpp = extlist; *cpp; cpp++) {
(void) strcpy(cp, *cpp);
if (stat(result, &st) == 0 && S_ISREG(st.st_mode))
return (result);
}
free(result);
return (NULL);
}
static int
file_havepath(const char *name)
{
const char *cp;
(void) archsw.arch_getdev(NULL, name, &cp);
return (cp != name || strchr(name, '/') != NULL);
}
static char *
file_search(const char *name, const char **extlist)
{
struct moduledir *mdp;
struct stat sb;
char *result;
int namelen;
if (name == NULL)
return (NULL);
if (*name == '\0')
return (strdup(name));
if (file_havepath(name)) {
if (stat(name, &sb) == 0)
return (strdup(name));
return (NULL);
}
moduledir_rebuild();
result = NULL;
namelen = strlen(name);
STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
result = file_lookup(mdp->d_path, name, namelen, extlist);
if (result != NULL)
break;
}
return (result);
}
#define INT_ALIGN(base, ptr) ptr = \
(base) + (((ptr) - (base) + sizeof (int) - 1) & ~(sizeof (int) - 1))
static char *
mod_search_hints(struct moduledir *mdp, const char *modname,
struct mod_depend *verinfo)
{
uchar_t *cp, *recptr, *bufend, *best;
char *result;
int *intp, bestver, blen, clen, ival, modnamelen, reclen;
bool found;
moduledir_readhints(mdp);
modnamelen = strlen(modname);
found = false;
result = NULL;
bestver = 0;
if (mdp->d_hints == NULL)
goto bad;
recptr = mdp->d_hints;
bufend = recptr + mdp->d_hintsz;
clen = blen = 0;
best = cp = NULL;
while (recptr < bufend && !found) {
intp = (int *)recptr;
reclen = *intp++;
ival = *intp++;
cp = (uchar_t *)intp;
switch (ival) {
case MDT_VERSION:
clen = *cp++;
if (clen != modnamelen || bcmp(cp, modname, clen) != 0)
break;
cp += clen;
INT_ALIGN(mdp->d_hints, cp);
ival = *(int *)cp;
cp += sizeof (int);
clen = *cp++;
if (verinfo == NULL ||
ival == verinfo->md_ver_preferred) {
found = true;
break;
}
if (ival >= verinfo->md_ver_minimum &&
ival <= verinfo->md_ver_maximum &&
ival > bestver) {
bestver = ival;
best = cp;
blen = clen;
}
break;
default:
break;
}
recptr += reclen + sizeof (int);
}
if (found)
result = file_lookup(mdp->d_path, (char *)cp, clen, NULL);
else if (best)
result = file_lookup(mdp->d_path, (char *)best, blen, NULL);
bad:
if (!found && bestver == 0 && result == NULL) {
result = file_lookup(mdp->d_path, modname, modnamelen,
kld_ext_list);
}
return (result);
}
static char *
mod_searchmodule(char *name, struct mod_depend *verinfo)
{
struct moduledir *mdp;
char *result;
moduledir_rebuild();
result = NULL;
STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
result = mod_search_hints(mdp, name, verinfo);
if (result != NULL)
break;
}
return (result);
}
int
file_addmodule(struct preloaded_file *fp, char *modname, int version,
struct kernel_module **newmp)
{
struct kernel_module *mp;
struct mod_depend mdepend;
bzero(&mdepend, sizeof (mdepend));
mdepend.md_ver_preferred = version;
mp = file_findmodule(fp, modname, &mdepend);
if (mp != NULL)
return (EEXIST);
mp = calloc(1, sizeof (struct kernel_module));
if (mp == NULL)
return (ENOMEM);
mp->m_name = strdup(modname);
if (mp->m_name == NULL) {
free(mp);
return (ENOMEM);
}
mp->m_version = version;
mp->m_fp = fp;
mp->m_next = fp->f_modules;
fp->f_modules = mp;
if (newmp)
*newmp = mp;
return (0);
}
void
file_discard(struct preloaded_file *fp)
{
struct file_metadata *md, *md1;
struct kernel_module *mp, *mp1;
if (fp == NULL)
return;
if (archsw.arch_free_loadaddr != NULL && fp->f_addr &&
fp->f_size != 0) {
archsw.arch_free_loadaddr(fp->f_addr,
(uint64_t)(roundup2(fp->f_size, PAGE_SIZE) >> 12));
}
md = fp->f_metadata;
while (md != NULL) {
md1 = md;
md = md->md_next;
free(md1);
}
mp = fp->f_modules;
while (mp != NULL) {
free(mp->m_name);
mp1 = mp;
mp = mp->m_next;
free(mp1);
}
free(fp->f_name);
free(fp->f_type);
free(fp->f_args);
free(fp);
}
struct preloaded_file *
file_alloc(void)
{
return (calloc(1, sizeof (struct preloaded_file)));
}
static void
file_insert_tail(struct preloaded_file *fp)
{
struct preloaded_file *cm;
fp->f_next = NULL;
if (preloaded_files == NULL) {
preloaded_files = fp;
} else {
for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next)
;
cm->f_next = fp;
}
}
static void
file_remove(struct preloaded_file *fp)
{
struct preloaded_file *cm;
if (preloaded_files == NULL)
return;
if (preloaded_files == fp) {
preloaded_files = fp->f_next;
return;
}
for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next) {
if (cm->f_next == fp) {
cm->f_next = fp->f_next;
return;
}
}
}
static char *
moduledir_fullpath(struct moduledir *mdp, const char *fname)
{
char *cp;
cp = malloc(strlen(mdp->d_path) + strlen(fname) + 2);
if (cp == NULL)
return (NULL);
strcpy(cp, mdp->d_path);
strcat(cp, "/");
strcat(cp, fname);
return (cp);
}
static void
moduledir_readhints(struct moduledir *mdp)
{
struct stat st;
char *path;
int fd, size, version;
if (mdp->d_hints != NULL || (mdp->d_flags & MDIR_NOHINTS))
return;
path = moduledir_fullpath(mdp, "linker.hints");
if (stat(path, &st) != 0 ||
st.st_size < (ssize_t)(sizeof (version) + sizeof (int)) ||
st.st_size > LINKER_HINTS_MAX ||
(fd = open(path, O_RDONLY)) < 0) {
free(path);
mdp->d_flags |= MDIR_NOHINTS;
return;
}
free(path);
size = read(fd, &version, sizeof (version));
if (size != sizeof (version) || version != LINKER_HINTS_VERSION)
goto bad;
size = st.st_size - size;
mdp->d_hints = malloc(size);
if (mdp->d_hints == NULL)
goto bad;
if (read(fd, mdp->d_hints, size) != size)
goto bad;
mdp->d_hintsz = size;
(void) close(fd);
return;
bad:
(void) close(fd);
free(mdp->d_hints);
mdp->d_hints = NULL;
mdp->d_flags |= MDIR_NOHINTS;
}
static void
moduledir_rebuild(void)
{
struct moduledir *mdp, *mtmp;
const char *path, *cp, *ep;
size_t cplen;
path = getenv("module_path");
if (path == NULL)
path = default_searchpath;
STAILQ_FOREACH(mdp, &moduledir_list, d_link)
mdp->d_flags |= MDIR_REMOVED;
for (ep = path; *ep != 0; ep++) {
cp = ep;
for (; *ep != 0 && *ep != ';'; ep++)
;
for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/';
cplen--)
;
STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
if (strlen(mdp->d_path) != cplen ||
bcmp(cp, mdp->d_path, cplen) != 0)
continue;
mdp->d_flags &= ~MDIR_REMOVED;
break;
}
if (mdp == NULL) {
mdp = malloc(sizeof (*mdp) + cplen + 1);
if (mdp == NULL)
return;
mdp->d_path = (char *)(mdp + 1);
bcopy(cp, mdp->d_path, cplen);
mdp->d_path[cplen] = 0;
mdp->d_hints = NULL;
mdp->d_flags = 0;
STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link);
}
if (*ep == '\0')
break;
}
mdp = STAILQ_FIRST(&moduledir_list);
while (mdp) {
if ((mdp->d_flags & MDIR_REMOVED) == 0) {
mdp = STAILQ_NEXT(mdp, d_link);
} else {
free(mdp->d_hints);
mtmp = mdp;
mdp = STAILQ_NEXT(mdp, d_link);
STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link);
free(mtmp);
}
}
}