#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_syscall.c,v 1.23 2026/02/01 03:32:44 riastradh Exp $");
#ifdef _KERNEL_OPT
#include "opt_dtrace.h"
#include "opt_ktrace.h"
#include "opt_modular.h"
#include "opt_ptrace.h"
#include "opt_syscall_debug.h"
#endif
#define SYSVSHM
#define SYSVSEM
#define SYSVMSG
#include <sys/param.h>
#include <sys/ktrace.h>
#include <sys/module.h>
#include <sys/ptrace.h>
#include <sys/sched.h>
#include <sys/sdt.h>
#include <sys/syscall.h>
#include <sys/syscallargs.h>
#include <sys/syscallvar.h>
#include <sys/systm.h>
#include <sys/xcall.h>
int
sys_nomodule(struct lwp *l, const void *v, register_t *retval)
{
#ifdef MODULAR
const struct sysent *sy;
const struct emul *em;
const struct sc_autoload *auto_list;
u_int code;
kernconfig_lock();
sy = l->l_sysent;
if (sy->sy_call != sys_nomodule) {
kernconfig_unlock();
return SET_ERROR(ERESTART);
}
em = l->l_proc->p_emul;
code = sy - em->e_sysent;
if ((auto_list = em->e_sc_autoload) != NULL)
for (; auto_list->al_code > 0; auto_list++) {
if (auto_list->al_code != code) {
continue;
}
if (module_autoload(auto_list->al_module,
MODULE_CLASS_ANY) != 0 ||
sy->sy_call == sys_nomodule) {
break;
}
kernconfig_unlock();
return SET_ERROR(ERESTART);
}
kernconfig_unlock();
#endif
return sys_nosys(l, v, retval);
}
int
syscall_establish(const struct emul *em, const struct syscall_package *sp)
{
struct sysent *sy;
int i;
KASSERT(kernconfig_is_held());
if (em == NULL) {
em = &emul_netbsd;
}
sy = em->e_sysent;
for (i = 0; sp[i].sp_call != NULL; i++) {
if (sp[i].sp_code >= SYS_NSYSENT)
return SET_ERROR(EINVAL);
if (sy[sp[i].sp_code].sy_call != sys_nomodule &&
sy[sp[i].sp_code].sy_call != sys_nosys) {
#ifdef DIAGNOSTIC
printf("syscall %d is busy\n", sp[i].sp_code);
#endif
return SET_ERROR(EBUSY);
}
}
for (i = 0; sp[i].sp_call != NULL; i++) {
sy[sp[i].sp_code].sy_call = sp[i].sp_call;
}
return 0;
}
int
syscall_disestablish(const struct emul *em, const struct syscall_package *sp)
{
struct sysent *sy;
const uint32_t *sb;
lwp_t *l;
int i;
KASSERT(kernconfig_is_held());
if (em == NULL) {
em = &emul_netbsd;
}
sy = em->e_sysent;
sb = em->e_nomodbits;
for (i = 0; sp[i].sp_call != NULL; i++) {
KASSERT(sy[sp[i].sp_code].sy_call == sp[i].sp_call);
sy[sp[i].sp_code].sy_call =
sb[sp[i].sp_code / 32] & (1 << (sp[i].sp_code % 32)) ?
sys_nomodule : sys_nosys;
}
xc_barrier(0);
for (i = 0; sp[i].sp_call != NULL; i++) {
mutex_enter(&proc_lock);
LIST_FOREACH(l, &alllwp, l_list) {
if (l->l_sysent == &sy[sp[i].sp_code]) {
break;
}
}
mutex_exit(&proc_lock);
if (l == NULL) {
continue;
}
for (i = 0; sp[i].sp_call != NULL; i++) {
sy[sp[i].sp_code].sy_call = sp[i].sp_call;
}
return SET_ERROR(EBUSY);
}
return 0;
}
bool
trace_is_enabled(struct proc *p)
{
#ifdef SYSCALL_DEBUG
return (true);
#endif
#ifdef KTRACE
if (ISSET(p->p_traceflag, (KTRFAC_SYSCALL | KTRFAC_SYSRET)))
return (true);
#endif
#ifdef PTRACE
if (ISSET(p->p_slflag, PSL_SYSCALL))
return (true);
#endif
return (false);
}
int
trace_enter(register_t code, const struct sysent *sy, const void *args)
{
int error = 0;
#if defined(PTRACE) || defined(KDTRACE_HOOKS)
struct proc *p = curlwp->l_proc;
#endif
#ifdef KDTRACE_HOOKS
if (sy->sy_entry) {
struct emul *e = p->p_emul;
if (e->e_dtrace_syscall)
(*e->e_dtrace_syscall)(sy->sy_entry, code, sy, args,
NULL, 0);
}
#endif
#ifdef SYSCALL_DEBUG
scdebug_call(code, args);
#endif
ktrsyscall(code, args, sy->sy_narg);
#ifdef PTRACE
if ((p->p_slflag & (PSL_SYSCALL|PSL_TRACED)) ==
(PSL_SYSCALL|PSL_TRACED)) {
proc_stoptrace(TRAP_SCE, code, args, NULL, 0);
if (curlwp->l_proc->p_slflag & PSL_SYSCALLEMU) {
error = SET_ERROR(EJUSTRETURN);
}
}
#endif
return error;
}
void
trace_exit(register_t code, const struct sysent *sy, const void *args,
register_t rval[], int error)
{
#if defined(PTRACE) || defined(KDTRACE_HOOKS)
struct proc *p = curlwp->l_proc;
#endif
#ifdef KDTRACE_HOOKS
if (sy->sy_return) {
struct emul *e = p->p_emul;
if (e->e_dtrace_syscall)
(*p->p_emul->e_dtrace_syscall)(sy->sy_return, code, sy,
args, rval, error);
}
#endif
#ifdef SYSCALL_DEBUG
scdebug_ret(code, error, rval);
#endif
ktrsysret(code, error, rval);
#ifdef PTRACE
if ((p->p_slflag & (PSL_SYSCALL|PSL_TRACED|PSL_SYSCALLEMU)) ==
(PSL_SYSCALL|PSL_TRACED)) {
proc_stoptrace(TRAP_SCX, code, args, rval, error);
}
CLR(p->p_slflag, PSL_SYSCALLEMU);
#endif
}