1701 lines · 42.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* *  linux/init/main.c * *  Copyright (C) 1991, 1992  Linus Torvalds * *  GK 2/5/95  -  Changed to support mounting root fs via NFS *  Added initrd & change_root: Werner Almesberger & Hans Lermen, Feb '96 *  Moan early if gcc is old, avoiding bogus kernels - Paul Gortmaker, May '96 *  Simplified starting of init:  Michael A. Griffith <grif@acm.org> */ #define DEBUG		/* Enable initcall_debug */ #include <linux/types.h>#include <linux/export.h>#include <linux/extable.h>#include <linux/module.h>#include <linux/proc_fs.h>#include <linux/binfmts.h>#include <linux/kernel.h>#include <linux/syscalls.h>#include <linux/stackprotector.h>#include <linux/string.h>#include <linux/ctype.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/initrd.h>#include <linux/memblock.h>#include <linux/acpi.h>#include <linux/bootconfig.h>#include <linux/console.h>#include <linux/nmi.h>#include <linux/percpu.h>#include <linux/kmod.h>#include <linux/kprobes.h>#include <linux/kmsan.h>#include <linux/ksysfs.h>#include <linux/vmalloc.h>#include <linux/kernel_stat.h>#include <linux/start_kernel.h>#include <linux/security.h>#include <linux/smp.h>#include <linux/profile.h>#include <linux/kfence.h>#include <linux/rcupdate.h>#include <linux/srcu.h>#include <linux/moduleparam.h>#include <linux/kallsyms.h>#include <linux/buildid.h>#include <linux/writeback.h>#include <linux/cpu.h>#include <linux/cpuset.h>#include <linux/memcontrol.h>#include <linux/cgroup.h>#include <linux/tick.h>#include <linux/sched/isolation.h>#include <linux/interrupt.h>#include <linux/taskstats_kern.h>#include <linux/delayacct.h>#include <linux/unistd.h>#include <linux/utsname.h>#include <linux/rmap.h>#include <linux/mempolicy.h>#include <linux/key.h>#include <linux/debug_locks.h>#include <linux/debugobjects.h>#include <linux/lockdep.h>#include <linux/kmemleak.h>#include <linux/padata.h>#include <linux/pid_namespace.h>#include <linux/device/driver.h>#include <linux/kthread.h>#include <linux/sched.h>#include <linux/sched/init.h>#include <linux/signal.h>#include <linux/idr.h>#include <linux/kgdb.h>#include <linux/ftrace.h>#include <linux/async.h>#include <linux/shmem_fs.h>#include <linux/slab.h>#include <linux/perf_event.h>#include <linux/ptrace.h>#include <linux/pti.h>#include <linux/blkdev.h>#include <linux/sched/clock.h>#include <linux/sched/task.h>#include <linux/sched/task_stack.h>#include <linux/context_tracking.h>#include <linux/random.h>#include <linux/moduleloader.h>#include <linux/list.h>#include <linux/integrity.h>#include <linux/proc_ns.h>#include <linux/io.h>#include <linux/cache.h>#include <linux/rodata_test.h>#include <linux/jump_label.h>#include <linux/kcsan.h>#include <linux/init_syscalls.h>#include <linux/stackdepot.h>#include <linux/randomize_kstack.h>#include <linux/pidfs.h>#include <linux/fs_struct.h>#include <linux/ptdump.h>#include <linux/time_namespace.h>#include <linux/unaligned.h>#include <linux/vdso_datastore.h>#include <net/net_namespace.h> #include <asm/io.h>#include <asm/setup.h>#include <asm/sections.h>#include <asm/cacheflush.h> #define CREATE_TRACE_POINTS#include <trace/events/initcall.h> #include <kunit/test.h> static int kernel_init(void *); /* * Debug helper: via this flag we know that we are in 'early bootup code' * where only the boot processor is running with IRQ disabled.  This means * two things - IRQ must not be enabled before the flag is cleared and some * operations which are not allowed with IRQ disabled are allowed while the * flag is set. */bool early_boot_irqs_disabled __read_mostly; enum system_states system_state __read_mostly;EXPORT_SYMBOL(system_state); /* * Boot command-line arguments */#define MAX_INIT_ARGS CONFIG_INIT_ENV_ARG_LIMIT#define MAX_INIT_ENVS CONFIG_INIT_ENV_ARG_LIMIT /* Default late time init is NULL. archs can override this later. */void (*__initdata late_time_init)(void); /* Untouched command line saved by arch-specific code. */char __initdata boot_command_line[COMMAND_LINE_SIZE];/* Untouched saved command line (eg. for /proc) */char *saved_command_line __ro_after_init;unsigned int saved_command_line_len __ro_after_init;/* Command line for parameter parsing */static char *static_command_line;/* Untouched extra command line */static char *extra_command_line;/* Extra init arguments */static char *extra_init_args; #ifdef CONFIG_BOOT_CONFIG/* Is bootconfig on command line? */static bool bootconfig_found;static size_t initargs_offs;#else# define bootconfig_found false# define initargs_offs 0#endif static char *execute_command;static char *ramdisk_execute_command = "/init";static bool __initdata ramdisk_execute_command_set; /* * Used to generate warnings if static_key manipulation functions are used * before jump_label_init is called. */bool static_key_initialized __read_mostly;EXPORT_SYMBOL_GPL(static_key_initialized); /* * If set, this is an indication to the drivers that reset the underlying * device before going ahead with the initialization otherwise driver might * rely on the BIOS and skip the reset operation. * * This is useful if kernel is booting in an unreliable environment. * For ex. kdump situation where previous kernel has crashed, BIOS has been * skipped and devices will be in unknown state. */unsigned int reset_devices;EXPORT_SYMBOL(reset_devices); static int __init set_reset_devices(char *str){	reset_devices = 1;	return 1;} __setup("reset_devices", set_reset_devices); static const char *argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };const char *envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };static const char *panic_later, *panic_param; static bool __init obsolete_checksetup(char *line){	const struct obs_kernel_param *p;	bool had_early_param = false; 	p = __setup_start;	do {		int n = strlen(p->str);		if (parameqn(line, p->str, n)) {			if (p->early) {				/* Already done in parse_early_param?				 * (Needs exact match on param part).				 * Keep iterating, as we can have early				 * params and __setups of same names 8( */				if (line[n] == '\0' || line[n] == '=')					had_early_param = true;			} else if (!p->setup_func) {				pr_warn("Parameter %s is obsolete, ignored\n",					p->str);				return true;			} else if (p->setup_func(line + n))				return true;		}		p++;	} while (p < __setup_end); 	return had_early_param;} /* * This should be approx 2 Bo*oMips to start (note initial shift), and will * still work even if initially too large, it will just take slightly longer */unsigned long loops_per_jiffy = (1<<12);EXPORT_SYMBOL(loops_per_jiffy); static int __init debug_kernel(char *str){	console_loglevel = CONSOLE_LOGLEVEL_DEBUG;	return 0;} static int __init quiet_kernel(char *str){	console_loglevel = CONSOLE_LOGLEVEL_QUIET;	return 0;} early_param("debug", debug_kernel);early_param("quiet", quiet_kernel); static int __init loglevel(char *str){	int newlevel; 	/*	 * Only update loglevel value when a correct setting was passed,	 * to prevent blind crashes (when loglevel being set to 0) that	 * are quite hard to debug	 */	if (get_option(&str, &newlevel)) {		console_loglevel = newlevel;		return 0;	} 	return -EINVAL;} early_param("loglevel", loglevel); #ifdef CONFIG_BLK_DEV_INITRDstatic void * __init get_boot_config_from_initrd(size_t *_size){	u32 size, csum;	char *data;	u8 *hdr;	int i; 	if (!initrd_end)		return NULL; 	data = (char *)initrd_end - BOOTCONFIG_MAGIC_LEN;	/*	 * Since Grub may align the size of initrd to 4, we must	 * check the preceding 3 bytes as well.	 */	for (i = 0; i < 4; i++) {		if (!memcmp(data, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN))			goto found;		data--;	}	return NULL; found:	hdr = (u8 *)(data - 8);	size = get_unaligned_le32(hdr);	csum = get_unaligned_le32(hdr + 4); 	data = ((void *)hdr) - size;	if ((unsigned long)data < initrd_start) {		pr_err("bootconfig size %d is greater than initrd size %ld\n",			size, initrd_end - initrd_start);		return NULL;	} 	if (xbc_calc_checksum(data, size) != csum) {		pr_err("bootconfig checksum failed\n");		return NULL;	} 	/* Remove bootconfig from initramfs/initrd */	initrd_end = (unsigned long)data;	if (_size)		*_size = size; 	return data;}#elsestatic void * __init get_boot_config_from_initrd(size_t *_size){	return NULL;}#endif #ifdef CONFIG_BOOT_CONFIG /* Make an extra command line under given key word */static char * __init xbc_make_cmdline(const char *key){	struct xbc_node *root;	char *new_cmdline;	int ret, len = 0; 	root = xbc_find_node(key);	if (!root)		return NULL; 	/* Count required buffer size */	len = xbc_snprint_cmdline(NULL, 0, root);	if (len <= 0)		return NULL; 	new_cmdline = memblock_alloc(len + 1, SMP_CACHE_BYTES);	if (!new_cmdline) {		pr_err("Failed to allocate memory for extra kernel cmdline.\n");		return NULL;	} 	ret = xbc_snprint_cmdline(new_cmdline, len + 1, root);	if (ret < 0 || ret > len) {		pr_err("Failed to print extra kernel cmdline.\n");		memblock_free(new_cmdline, len + 1);		return NULL;	} 	return new_cmdline;} static int __init warn_bootconfig(char *str){	/* The 'bootconfig' option is handled by setup_boot_config(). */	return 0;} static void __init setup_boot_config(void){	const char *msg, *data;	int pos, ret, offs;	size_t size;	bool from_embedded = false; 	/* Cut out the bootconfig data even if we have no bootconfig option */	data = get_boot_config_from_initrd(&size);	/* If there is no bootconfig in initrd, try embedded one. */	if (!data) {		data = xbc_get_embedded_bootconfig(&size);		from_embedded = true;	} 	bootconfig_found = bootconfig_cmdline_requested(boot_command_line, &offs);	if (!(bootconfig_found || IS_ENABLED(CONFIG_BOOT_CONFIG_FORCE)))		return; 	/* Offset of the init arguments after a "--", located by the helper. */	initargs_offs = offs; 	if (!data) {		/* If user intended to use bootconfig, show an error level message */		if (bootconfig_found)			pr_err("'bootconfig' found on command line, but no bootconfig found\n");		else			pr_info("No bootconfig data provided, so skipping bootconfig");		return;	} 	if (size >= XBC_DATA_MAX) {		pr_err("bootconfig size %ld greater than max size %d\n",			(long)size, XBC_DATA_MAX);		return;	} 	ret = xbc_init(data, size, &msg, &pos);	if (ret < 0) {		if (pos < 0)			pr_err("Failed to init bootconfig: %s.\n", msg);		else			pr_err("Failed to parse bootconfig: %s at %d.\n",				msg, pos);	} else {		xbc_get_info(&ret, NULL);		pr_info("Load bootconfig: %ld bytes %d nodes\n", (long)size, ret);		/*		 * keys starting with "kernel." are passed via cmdline. When		 * this bootconfig came from the embedded source and		 * setup_arch() already prepended the rendered "kernel" subtree		 * to boot_command_line, rendering again here would duplicate		 * the keys in saved_command_line and make accumulating handlers		 * (console=, earlycon=, ...) re-register the same value. Skip		 * only when the prepend really happened.		 *		 * On arches that do not select ARCH_SUPPORTS_CMDLINE_FROM_BOOTCONFIG,		 * CONFIG_CMDLINE_FROM_BOOTCONFIG is unselectable and		 * xbc_embedded_cmdline_applied() collapses to a stub returning		 * false, so this path still runs and the embedded "kernel"		 * keys reach the cmdline via the runtime parser exactly as		 * before this series.		 */		if (!from_embedded || !xbc_embedded_cmdline_applied())			extra_command_line = xbc_make_cmdline("kernel");		/* Also, "init." keys are init arguments */		extra_init_args = xbc_make_cmdline("init");	}	return;} static void __init exit_boot_config(void){	xbc_exit();} #else	/* !CONFIG_BOOT_CONFIG */ static void __init setup_boot_config(void){	/* Remove bootconfig data from initrd */	get_boot_config_from_initrd(NULL);} static int __init warn_bootconfig(char *str){	pr_warn("WARNING: 'bootconfig' found on the kernel command line but CONFIG_BOOT_CONFIG is not set.\n");	return 0;} #define exit_boot_config()	do {} while (0) #endif	/* CONFIG_BOOT_CONFIG */ early_param("bootconfig", warn_bootconfig); bool __init cmdline_has_extra_options(void){	return extra_command_line || extra_init_args;} /* Change NUL term back to "=", to make "param" the whole string. */static void __init repair_env_string(char *param, char *val){	if (val) {		/* param=val or param="val"? */		if (val == param+strlen(param)+1)			val[-1] = '=';		else if (val == param+strlen(param)+2) {			val[-2] = '=';			memmove(val-1, val, strlen(val)+1);		} else			BUG();	}} /* Anything after -- gets handed straight to init. */static int __init set_init_arg(char *param, char *val,			       const char *unused, void *arg){	unsigned int i; 	if (panic_later)		return 0; 	repair_env_string(param, val); 	for (i = 0; argv_init[i]; i++) {		if (i == MAX_INIT_ARGS) {			panic_later = "init";			panic_param = param;			return 0;		}	}	argv_init[i] = param;

Lines 1–500 of 1701.