#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: init_main.c,v 1.552 2025/05/08 05:31:16 imil Exp $");
#include "opt_cnmagic.h"
#include "opt_ddb.h"
#include "opt_inet.h"
#include "opt_ipsec.h"
#include "opt_modular.h"
#include "opt_ntp.h"
#include "opt_pipe.h"
#include "opt_syscall_debug.h"
#include "opt_sysv.h"
#include "opt_fileassoc.h"
#include "opt_ktrace.h"
#include "opt_pax.h"
#include "opt_compat_netbsd.h"
#include "opt_ptrace.h"
#include "opt_splash.h"
#include "opt_kernhist.h"
#include "opt_gprof.h"
#if defined(SPLASHSCREEN) && defined(makeoptions_SPLASHSCREEN_IMAGE)
extern void *_binary_splash_image_start;
extern void *_binary_splash_image_end;
#endif
#include "ksyms.h"
#include "veriexec.h"
#include <sys/param.h>
#include <sys/acct.h>
#include <sys/filedesc.h>
#include <sys/file.h>
#include <sys/errno.h>
#include <sys/callout.h>
#include <sys/cpu.h>
#include <sys/cpufreq.h>
#include <sys/spldebug.h>
#include <sys/kernel.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/lwp.h>
#include <sys/kthread.h>
#include <sys/resourcevar.h>
#include <sys/signalvar.h>
#include <sys/systm.h>
#include <sys/vnode.h>
#include <sys/fstrans.h>
#include <sys/tty.h>
#include <sys/conf.h>
#include <sys/disklabel.h>
#include <sys/buf.h>
#include <sys/device.h>
#include <sys/exec.h>
#include <sys/socketvar.h>
#include <sys/protosw.h>
#include <sys/percpu.h>
#include <sys/pserialize.h>
#include <sys/pset.h>
#include <sys/sysctl.h>
#include <sys/reboot.h>
#include <sys/event.h>
#include <sys/mbuf.h>
#include <sys/sched.h>
#include <sys/sleepq.h>
#include <sys/ipi.h>
#include <sys/iostat.h>
#include <sys/vmem.h>
#include <sys/uuid.h>
#include <sys/extent.h>
#include <sys/disk.h>
#include <sys/msgbuf.h>
#include <sys/module.h>
#include <sys/module_hook.h>
#include <sys/event.h>
#include <sys/lockf.h>
#include <sys/once.h>
#include <sys/kcpuset.h>
#include <sys/ksyms.h>
#include <sys/uidinfo.h>
#include <sys/kprintf.h>
#include <sys/bufq.h>
#include <sys/threadpool.h>
#include <sys/futex.h>
#ifdef IPSEC
#include <netipsec/ipsec.h>
#endif
#include <sys/domain.h>
#include <sys/namei.h>
#include <sys/rnd.h>
#include <sys/pipe.h>
#if NVERIEXEC > 0
#include <sys/verified_exec.h>
#endif
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
#include <sys/kauth.h>
#include <net80211/ieee80211_netbsd.h>
#include <sys/cprng.h>
#include <sys/psref.h>
#include <sys/radixtree.h>
#include <sys/heartbeat.h>
#include <sys/syscall.h>
#include <sys/syscallargs.h>
#include <sys/pax.h>
#include <dev/clock_subr.h>
#include <secmodel/secmodel.h>
#include <ufs/ufs/quota.h>
#include <miscfs/genfs/genfs.h>
#include <miscfs/specfs/specdev.h>
#include <sys/cpu.h>
#include <uvm/uvm.h>
#include <dev/cons.h>
#include <dev/splash/splash.h>
#include <net/bpf.h>
#include <net/if.h>
#include <net/pfil.h>
#include <net/raw_cb.h>
#include <net/if_llatbl.h>
#include <prop/proplib.h>
#include <sys/userconf.h>
#ifdef BOOT_DURATION
#include <sys/boot_duration.h>
#endif
extern time_t rootfstime;
#ifndef curlwp
struct lwp *curlwp = &lwp0;
#endif
struct proc *initproc;
struct vnode *rootvp, *swapdev_vp;
int boothowto;
int cold __read_mostly = 1;
int shutting_down __read_mostly;
int start_init_exec;
static void check_console(struct lwp *l);
static void start_init(void *);
static void configure(void);
static void configure2(void);
static void configure3(void);
void main(void);
void
main(void)
{
struct timespec time;
struct lwp *l;
struct proc *p;
int s, error;
#ifdef NVNODE_IMPLICIT
int usevnodes;
#endif
CPU_INFO_ITERATOR cii;
struct cpu_info *ci;
#ifdef DIAGNOSTIC
int cpucount = 0;
for (CPU_INFO_FOREACH(cii, ci)) {
KASSERT(ci == curcpu());
cpucount++;
}
KASSERT(cpucount == 1);
#endif
l = &lwp0;
#ifndef LWP0_CPU_INFO
l->l_cpu = curcpu();
#endif
l->l_pflag |= LP_RUNNING;
consinit();
#ifdef CNMAGIC
cn_set_magic(CNMAGIC);
#endif
kernel_lock_init();
once_init();
todr_init();
mi_cpu_init();
kernconfig_lock_init();
kthread_sysinit();
devsw_init();
evcnt_init();
uvm_init();
ubchist_init();
kcpuset_sysinit();
prop_kern_init();
#if ((NKSYMS > 0) || (NDDB > 0) || (NMODULAR > 0))
ksyms_init();
#endif
kprintf_init();
percpu_init();
radix_tree_init();
pserialize_init();
extent_init();
cpu_startup();
sysctl_init();
callout_startup();
kauth_init();
secmodel_init();
spec_init();
bpf_setops();
ipi_sysinit();
module_init();
module_hook_init();
module_init_class(MODULE_CLASS_SECMODEL);
bufinit();
biohist_init();
#ifdef KERNHIST
sysctl_kernhist_init();
#endif
#if defined(SPLASHSCREEN) && defined(makeoptions_SPLASHSCREEN_IMAGE)
size_t splash_size = (&_binary_splash_image_end -
&_binary_splash_image_start) * sizeof(void *);
splash_setimage(&_binary_splash_image_start, splash_size);
#endif
soinit();
rnd_init();
cprng_init();
procinit();
lwpinit();
psref_init();
exec_init(1);
signal_init();
resource_init();
proc0_init();
lwp0_init();
kpreempt_disable();
threadpools_init();
uid_init();
(void)chgproccnt(0, 1);
sched_rqinit();
turnstile_init();
sleeptab_init(&sleeptab);
sched_init();
psets_init();
cpufreq_init();
error = mi_cpu_attach(curcpu());
KASSERT(error == 0);
time_init();
mbinit();
iostat_init();
loginit();
module_start_unload_thread();
config_init_mi();
#ifdef NVNODE_IMPLICIT
usevnodes = calc_cache_size(vmem_size(kmem_arena, VMEM_FREE|VMEM_ALLOC),
10, VNODE_KMEM_MAXPCT) / VNODE_COST;
if (usevnodes > desiredvnodes)
desiredvnodes = usevnodes;
#endif
#ifdef MAXFILES_IMPLICIT
if (ctob((uint64_t)physmem) >= 16ULL * 1024 * 1024 * 1024)
maxfiles = MAX(maxfiles, 20000);
if (ctob((uint64_t)physmem) >= 1024 * 1024 * 1024)
maxfiles = MAX(maxfiles, 10000);
#endif
fstrans_init();
vfsinit();
lf_init();
fd_sys_init();
kqueue_init();
inittimecounter();
ntp_init();
tty_init();
ttyldisc_init();
bufinit2();
dkwedge_init();
pfil_init();
ifinit1();
spldebug_start();
soinit1();
bufq_init();
module_init_class(MODULE_CLASS_BUFQ);
configure();
#ifdef __HAVE_LEGACY_INTRCNT
evcnt_attach_legacy_intrcnt();
#endif
rnd_init_softint();
cprng_fast_init();
heartbeat_start();
ssp_init();
ubc_init();
mm_init();
configure2();
ipi_percpu_init();
futex_sys_init();
kpreempt_enable();
yield();
vmem_rehash_start();
#if NVERIEXEC > 0
veriexec_init();
#endif
pax_init();
#ifdef IPSEC
ipsec_attach();
#endif
s = splnet();
ifinit();
#if defined(INET) || defined(INET6)
lltableinit();
#endif
domaininit(true);
ifinit_post();
if_attachdomain();
splx(s);
#ifdef GPROF
kmstartup();
#endif
acct_init();
#ifndef PIPE_SOCKETPAIR
pipe_init();
#endif
#ifdef KTRACE
ktrinit();
#endif
machdep_init();
procinit_sysctl();
scdebug_init();
if (fork1(l, 0, SIGCHLD, NULL, 0, start_init, NULL, NULL))
panic("fork init");
mutex_enter(&proc_lock);
initproc = proc_find_raw(1);
mutex_exit(&proc_lock);
module_init_class(MODULE_CLASS_ANY);
module_builtin_require_force();
config_finalize();
sysctl_finalize();
cpu_rootconf();
cpu_dumpconf();
do {
domountroothook(root_device);
if ((error = vfs_mountroot())) {
printf("cannot mount root, error = %d\n", error);
boothowto |= RB_ASKNAME;
setroot(root_device,
(rootdev != NODEV) ? DISKPART(rootdev) : 0);
}
} while (error != 0);
mountroothook_destroy();
configure3();
inittodr(rootfstime);
getnanotime(&time);
mutex_enter(&proc_lock);
LIST_FOREACH(p, &allproc, p_list) {
KASSERT((p->p_flag & PK_MARKER) == 0);
mutex_enter(p->p_lock);
TIMESPEC_TO_TIMEVAL(&p->p_stats->p_start, &time);
LIST_FOREACH(l, &p->p_lwps, l_sibling) {
lwp_lock(l);
memset(&l->l_rtime, 0, sizeof(l->l_rtime));
lwp_unlock(l);
}
mutex_exit(p->p_lock);
}
mutex_exit(&proc_lock);
binuptime(&curlwp->l_stime);
for (CPU_INFO_FOREACH(cii, ci)) {
ci->ci_schedstate.spc_lastmod = time_second;
}
uvm_swap_init();
if (kthread_create(PRI_PGDAEMON, KTHREAD_MPSAFE, NULL, uvm_pageout,
NULL, NULL, "pgdaemon"))
panic("fork pagedaemon");
if (kthread_create(PRI_IOFLUSH, KTHREAD_MPSAFE, NULL, sched_sync,
NULL, NULL, "ioflush"))
panic("fork syncer");
config_finalize_mountroot();
mutex_enter(&proc_lock);
start_init_exec = 1;
cv_broadcast(&lbolt);
mutex_exit(&proc_lock);
#ifdef BOOT_DURATION
printf_nolog("kernel boot time: %"PRIu64"ms\n", boot_duration_timer());
#endif
uvm_scheduler();
}
static void
configure(void)
{
config_twiddle_init();
pmf_init();
module_init_class(MODULE_CLASS_DRIVER);
userconf_init();
if (boothowto & RB_USERCONF)
userconf_prompt();
if ((boothowto & (AB_SILENT|AB_VERBOSE)) == AB_SILENT) {
printf_nolog("Detecting hardware...");
}
cpu_configure();
}
static void
configure2(void)
{
CPU_INFO_ITERATOR cii;
struct cpu_info *ci;
int s;
cpu_topology_init();
initclocks();
cold = 0;
s = splsched();
curcpu()->ci_schedstate.spc_flags |= SPCF_RUNNING;
splx(s);
runq_init();
synch_init();
for (CPU_INFO_FOREACH(cii, ci)) {
uvm_cpu_attach(ci);
}
uvm_page_rebucket();
mp_online = true;
#if defined(MULTIPROCESSOR)
cpu_boot_secondary_processors();
#endif
config_twiddle_fn(NULL);
config_create_interruptthreads();
}
static void
configure3(void)
{
config_create_mountrootthreads();
yield();
}
static void
rootconf_handle_wedges(void)
{
struct disklabel label;
struct partition *p;
struct vnode *vp;
daddr_t startblk;
uint64_t nblks;
device_t dev;
int error;
if (booted_nblks) {
dev = booted_device;
startblk = booted_startblk;
nblks = booted_nblks;
} else {
vp = opendisk(booted_device);
if (vp == NULL)
return;
VOP_UNLOCK(vp);
error = VOP_IOCTL(vp, DIOCGDINFO, &label, FREAD, NOCRED);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
VOP_CLOSE(vp, FREAD, NOCRED);
vput(vp);
if (error)
return;
KASSERT(booted_partition >= 0
&& booted_partition < MAXPARTITIONS);
p = &label.d_partitions[booted_partition];
dev = booted_device;
startblk = p->p_offset;
nblks = p->p_size;
}
dev = dkwedge_find_partition(dev, startblk, nblks);
if (dev != NULL) {
booted_device = dev;
booted_partition = 0;
}
}
void
rootconf(void)
{
if (booted_device != NULL)
rootconf_handle_wedges();
setroot(booted_device, booted_partition);
}
static void
check_console(struct lwp *l)
{
struct vnode *vp;
int error;
error = namei_simple_kernel("/dev/console",
NSM_FOLLOW_NOEMULROOT, &vp);
if (error == 0) {
vrele(vp);
} else if (error == ENOENT) {
if (boothowto & (AB_VERBOSE|AB_DEBUG))
printf("warning: no /dev/console\n");
} else {
printf("warning: lookup /dev/console: error %d\n", error);
}
}
static const char * const initpaths[] = {
"/sbin/init",
"/sbin/oinit",
"/sbin/init.bak",
"/rescue/init",
NULL,
};
static void
start_init(void *arg)
{
struct lwp *l = arg;
struct proc *p = l->l_proc;
vaddr_t addr;
struct sys_execve_args
args;
int options, i, error;
register_t retval[2];
char flags[4], *flagsp;
const char *path, *slash;
char *ucp, **uap, *arg0, *arg1, *argv[3];
char ipath[129];
int ipx, len;
strncpy(p->p_comm, "init", MAXCOMLEN);
mutex_enter(&proc_lock);
while (start_init_exec == 0)
cv_wait(&lbolt, &proc_lock);
mutex_exit(&proc_lock);
check_console(l);
addr = (vaddr_t)STACK_ALLOC(USRSTACK, PAGE_SIZE);
if (uvm_map(&p->p_vmspace->vm_map, &addr, PAGE_SIZE,
NULL, UVM_UNKNOWN_OFFSET, 0,
UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_RW, UVM_INH_COPY,
UVM_ADV_NORMAL,
UVM_FLAG_FIXED|UVM_FLAG_OVERLAY|UVM_FLAG_COPYONW)) != 0)
panic("init: couldn't allocate argument space");
p->p_vmspace->vm_maxsaddr = (void *)STACK_MAX(addr, PAGE_SIZE);
ipx = 0;
while (1) {
if (boothowto & RB_ASKNAME) {
printf("init path");
if (initpaths[ipx])
printf(" (default %s)", initpaths[ipx]);
printf(": ");
len = cngetsn(ipath, sizeof(ipath)-1);
if (len == 4 && strcmp(ipath, "halt") == 0) {
kern_reboot(RB_HALT, NULL);
} else if (len == 6 && strcmp(ipath, "reboot") == 0) {
kern_reboot(0, NULL);
#if defined(DDB)
} else if (len == 3 && strcmp(ipath, "ddb") == 0) {
console_debugger();
continue;
#endif
} else if (len > 0 && ipath[0] == '/') {
ipath[len] = '\0';
path = ipath;
} else if (len == 0 && initpaths[ipx] != NULL) {
path = initpaths[ipx++];
} else {
printf("use absolute path, ");
#if defined(DDB)
printf("\"ddb\", ");
#endif
printf("\"halt\", or \"reboot\"\n");
continue;
}
} else {
if ((path = initpaths[ipx++]) == NULL) {
ipx = 0;
boothowto |= RB_ASKNAME;
continue;
}
}
ucp = (char *)USRSTACK;
flagsp = flags;
*flagsp++ = '-';
options = 0;
if (boothowto & RB_SINGLE) {
*flagsp++ = 's';
options = 1;
}
#ifdef notyet
if (boothowto & RB_FASTBOOT) {
*flagsp++ = 'f';
options = 1;
}
#endif
if (options != 0) {
*flagsp++ = '\0';
i = flagsp - flags;
#ifdef DEBUG
aprint_normal("init: copying out flags `%s' %d\n", flags, i);
#endif
arg1 = STACK_ALLOC(ucp, i);
ucp = STACK_MAX(arg1, i);
if ((error = copyout((void *)flags, arg1, i)) != 0)
goto copyerr;
} else
arg1 = NULL;
i = strlen(path) + 1;
#ifdef DEBUG
aprint_normal("init: copying out path `%s' %d\n", path, i);
#else
if (boothowto & RB_ASKNAME || path != initpaths[0])
printf("init: trying %s\n", path);
#endif
arg0 = STACK_ALLOC(ucp, i);
ucp = STACK_MAX(arg0, i);
if ((error = copyout(path, arg0, i)) != 0)
goto copyerr;
ucp = (void *)STACK_ALIGN(ucp, STACK_ALIGNBYTES);
uap = (char **)STACK_ALLOC(ucp, sizeof(argv));
SCARG(&args, path) = arg0;
SCARG(&args, argp) = uap;
SCARG(&args, envp) = NULL;
slash = strrchr(path, '/');
argv[0] = slash ? arg0 + (slash + 1 - path) : arg0;
argv[1] = arg1;
argv[2] = NULL;
if ((error = copyout(argv, uap, sizeof(argv))) != 0)
goto copyerr;
error = sys_execve(l, &args, retval);
if (error == 0 || error == EJUSTRETURN) {
KERNEL_UNLOCK_LAST(l);
return;
}
printf("exec %s: error %d\n", path, error);
}
printf("init: not found\n");
panic("no init");
copyerr:
panic("copyout %d", error);
}
vaddr_t
calc_cache_size(vsize_t vsize, int pct, int va_pct)
{
paddr_t t;
t = (uintmax_t)physmem * pct / 100 * PAGE_SIZE;
if (vsize != 0) {
vsize = (uintmax_t)vsize * va_pct / 100;
if (t > vsize) {
t = vsize;
}
}
return t;
}
#define MEM_PBUFSIZE sizeof("99999 MB")
void
banner(void)
{
static char notice[] = " Notice: this software is "
"protected by copyright";
char pbuf[81];
void (*pr)(const char *, ...) __printflike(1, 2);
int i;
if ((boothowto & AB_SILENT) != 0) {
snprintf(pbuf, sizeof(pbuf), "%s %s (%s)",
ostype, osrelease, kernel_ident);
printf_nolog("%s", pbuf);
for (i = 80 - strlen(pbuf) - sizeof(notice); i > 0; i--)
printf(" ");
printf_nolog("%s\n", notice);
pr = aprint_normal;
} else {
pr = printf;
}
memset(pbuf, 0, sizeof(pbuf));
(*pr)("%s%s", copyright, version);
format_bytes(pbuf, MEM_PBUFSIZE, ctob((uint64_t)physmem));
(*pr)("total memory = %s\n", pbuf);
format_bytes(pbuf, MEM_PBUFSIZE, ctob((uint64_t)uvm_availmem(false)));
(*pr)("avail memory = %s\n", pbuf);
}