325 lines
8 KiB
C
325 lines
8 KiB
C
/* $OpenBSD: kern_subr.c,v 1.52 2023/01/31 15:18:56 deraadt Exp $ */
|
|
/* $NetBSD: kern_subr.c,v 1.15 1996/04/09 17:21:56 ragge Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1982, 1986, 1991, 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. 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.
|
|
*
|
|
* @(#)kern_subr.c 8.3 (Berkeley) 1/21/94
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/sched.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/queue.h>
|
|
#include <uvm/uvm_extern.h>
|
|
|
|
#ifdef PMAP_CHECK_COPYIN
|
|
|
|
static inline int check_copyin(struct proc *, const void *, size_t);
|
|
extern int _copyinstr(const void *, void *, size_t, size_t *);
|
|
extern int _copyin(const void *uaddr, void *kaddr, size_t len);
|
|
|
|
/*
|
|
* If range overlaps an check_copyin region, return EFAULT
|
|
*/
|
|
static inline int
|
|
check_copyin(struct proc *p, const void *vstart, size_t len)
|
|
{
|
|
struct vm_map *map = &p->p_vmspace->vm_map;
|
|
const vaddr_t start = (vaddr_t)vstart;
|
|
const vaddr_t end = start + len;
|
|
int i, max;
|
|
|
|
/* XXX if the array was sorted, we could shortcut */
|
|
max = map->check_copyin_count;
|
|
membar_consumer();
|
|
for (i = 0; i < max; i++) {
|
|
vaddr_t s = map->check_copyin[i].start;
|
|
vaddr_t e = map->check_copyin[i].end;
|
|
if ((start >= s && start < e) || (end > s && end < e))
|
|
return EFAULT;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done)
|
|
{
|
|
size_t alen;
|
|
int error;
|
|
|
|
/*
|
|
* Must do the copyin checks after figuring out the string length,
|
|
* the buffer size length may cross into another ELF segment
|
|
*/
|
|
error = _copyinstr(uaddr, kaddr, len, &alen);
|
|
if (PMAP_CHECK_COPYIN && error == 0)
|
|
error = check_copyin(curproc, uaddr, alen);
|
|
if (done)
|
|
*done = alen;
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
copyin(const void *uaddr, void *kaddr, size_t len)
|
|
{
|
|
int error = 0;
|
|
|
|
if (PMAP_CHECK_COPYIN)
|
|
error = check_copyin(curproc, uaddr, len);
|
|
if (error == 0)
|
|
error = _copyin(uaddr, kaddr, len);
|
|
return (error);
|
|
}
|
|
#endif /* PMAP_CHECK_COPYIN */
|
|
|
|
int
|
|
uiomove(void *cp, size_t n, struct uio *uio)
|
|
{
|
|
struct iovec *iov;
|
|
size_t cnt;
|
|
int error = 0;
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (uio->uio_rw != UIO_READ && uio->uio_rw != UIO_WRITE)
|
|
panic("uiomove: mode");
|
|
if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
|
|
panic("uiomove: proc");
|
|
#endif
|
|
|
|
if (n > uio->uio_resid)
|
|
n = uio->uio_resid;
|
|
|
|
while (n > 0) {
|
|
iov = uio->uio_iov;
|
|
cnt = iov->iov_len;
|
|
if (cnt == 0) {
|
|
KASSERT(uio->uio_iovcnt > 0);
|
|
uio->uio_iov++;
|
|
uio->uio_iovcnt--;
|
|
continue;
|
|
}
|
|
if (cnt > n)
|
|
cnt = n;
|
|
switch (uio->uio_segflg) {
|
|
|
|
case UIO_USERSPACE:
|
|
sched_pause(preempt);
|
|
if (uio->uio_rw == UIO_READ)
|
|
error = copyout(cp, iov->iov_base, cnt);
|
|
else
|
|
error = copyin(iov->iov_base, cp, cnt);
|
|
if (error)
|
|
return (error);
|
|
break;
|
|
|
|
case UIO_SYSSPACE:
|
|
if (uio->uio_rw == UIO_READ)
|
|
error = kcopy(cp, iov->iov_base, cnt);
|
|
else
|
|
error = kcopy(iov->iov_base, cp, cnt);
|
|
if (error)
|
|
return(error);
|
|
}
|
|
iov->iov_base = (caddr_t)iov->iov_base + cnt;
|
|
iov->iov_len -= cnt;
|
|
uio->uio_resid -= cnt;
|
|
uio->uio_offset += cnt;
|
|
cp = (caddr_t)cp + cnt;
|
|
n -= cnt;
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Give next character to user as result of read.
|
|
*/
|
|
int
|
|
ureadc(int c, struct uio *uio)
|
|
{
|
|
struct iovec *iov;
|
|
|
|
if (uio->uio_resid == 0)
|
|
#ifdef DIAGNOSTIC
|
|
panic("ureadc: zero resid");
|
|
#else
|
|
return (EINVAL);
|
|
#endif
|
|
again:
|
|
if (uio->uio_iovcnt <= 0)
|
|
#ifdef DIAGNOSTIC
|
|
panic("ureadc: non-positive iovcnt");
|
|
#else
|
|
return (EINVAL);
|
|
#endif
|
|
iov = uio->uio_iov;
|
|
if (iov->iov_len <= 0) {
|
|
uio->uio_iovcnt--;
|
|
uio->uio_iov++;
|
|
goto again;
|
|
}
|
|
switch (uio->uio_segflg) {
|
|
|
|
case UIO_USERSPACE:
|
|
{
|
|
char tmp = c;
|
|
|
|
if (copyout(&tmp, iov->iov_base, sizeof(char)) != 0)
|
|
return (EFAULT);
|
|
}
|
|
break;
|
|
|
|
case UIO_SYSSPACE:
|
|
*(char *)iov->iov_base = c;
|
|
break;
|
|
}
|
|
iov->iov_base = (caddr_t)iov->iov_base + 1;
|
|
iov->iov_len--;
|
|
uio->uio_resid--;
|
|
uio->uio_offset++;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* General routine to allocate a hash table.
|
|
*/
|
|
void *
|
|
hashinit(int elements, int type, int flags, u_long *hashmask)
|
|
{
|
|
u_long hashsize, i;
|
|
LIST_HEAD(generic, generic) *hashtbl;
|
|
|
|
if (elements <= 0)
|
|
panic("hashinit: bad cnt");
|
|
if ((elements & (elements - 1)) == 0)
|
|
hashsize = elements;
|
|
else
|
|
for (hashsize = 1; hashsize < elements; hashsize <<= 1)
|
|
continue;
|
|
hashtbl = mallocarray(hashsize, sizeof(*hashtbl), type, flags);
|
|
if (hashtbl == NULL)
|
|
return NULL;
|
|
for (i = 0; i < hashsize; i++)
|
|
LIST_INIT(&hashtbl[i]);
|
|
*hashmask = hashsize - 1;
|
|
return (hashtbl);
|
|
}
|
|
|
|
void
|
|
hashfree(void *hash, int elements, int type)
|
|
{
|
|
u_long hashsize;
|
|
LIST_HEAD(generic, generic) *hashtbl = hash;
|
|
|
|
if (elements <= 0)
|
|
panic("hashfree: bad cnt");
|
|
if ((elements & (elements - 1)) == 0)
|
|
hashsize = elements;
|
|
else
|
|
for (hashsize = 1; hashsize < elements; hashsize <<= 1)
|
|
continue;
|
|
|
|
free(hashtbl, type, sizeof(*hashtbl) * hashsize);
|
|
}
|
|
|
|
/*
|
|
* "startup hook" types, functions, and variables.
|
|
*/
|
|
|
|
struct hook_desc_head startuphook_list =
|
|
TAILQ_HEAD_INITIALIZER(startuphook_list);
|
|
|
|
void *
|
|
hook_establish(struct hook_desc_head *head, int tail, void (*fn)(void *),
|
|
void *arg)
|
|
{
|
|
struct hook_desc *hdp;
|
|
|
|
hdp = malloc(sizeof(*hdp), M_DEVBUF, M_NOWAIT);
|
|
if (hdp == NULL)
|
|
return (NULL);
|
|
|
|
hdp->hd_fn = fn;
|
|
hdp->hd_arg = arg;
|
|
if (tail)
|
|
TAILQ_INSERT_TAIL(head, hdp, hd_list);
|
|
else
|
|
TAILQ_INSERT_HEAD(head, hdp, hd_list);
|
|
|
|
return (hdp);
|
|
}
|
|
|
|
void
|
|
hook_disestablish(struct hook_desc_head *head, void *vhook)
|
|
{
|
|
struct hook_desc *hdp;
|
|
|
|
#ifdef DIAGNOSTIC
|
|
for (hdp = TAILQ_FIRST(head); hdp != NULL;
|
|
hdp = TAILQ_NEXT(hdp, hd_list))
|
|
if (hdp == vhook)
|
|
break;
|
|
if (hdp == NULL)
|
|
return;
|
|
#endif
|
|
hdp = vhook;
|
|
TAILQ_REMOVE(head, hdp, hd_list);
|
|
free(hdp, M_DEVBUF, sizeof(*hdp));
|
|
}
|
|
|
|
/*
|
|
* Run hooks. Startup hooks are invoked right after scheduler_start but
|
|
* before root is mounted. Shutdown hooks are invoked immediately before the
|
|
* system is halted or rebooted, i.e. after file systems unmounted,
|
|
* after crash dump done, etc.
|
|
*/
|
|
void
|
|
dohooks(struct hook_desc_head *head, int flags)
|
|
{
|
|
struct hook_desc *hdp, *hdp_temp;
|
|
|
|
if ((flags & HOOK_REMOVE) == 0) {
|
|
TAILQ_FOREACH_SAFE(hdp, head, hd_list, hdp_temp) {
|
|
(*hdp->hd_fn)(hdp->hd_arg);
|
|
}
|
|
} else {
|
|
while ((hdp = TAILQ_FIRST(head)) != NULL) {
|
|
TAILQ_REMOVE(head, hdp, hd_list);
|
|
(*hdp->hd_fn)(hdp->hd_arg);
|
|
if ((flags & HOOK_FREE) != 0)
|
|
free(hdp, M_DEVBUF, sizeof(*hdp));
|
|
}
|
|
}
|
|
}
|