1535 lines · 36.9 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/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/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/rcupdate.h>#include <linux/moduleparam.h>#include <linux/kallsyms.h>#include <linux/writeback.h>#include <linux/cpu.h>#include <linux/cpuset.h>#include <linux/cgroup.h>#include <linux/efi.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/buffer_head.h>#include <linux/page_ext.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/sfi.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/elevator.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/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/mem_encrypt.h>#include <linux/kcsan.h>#include <linux/init_syscalls.h> #include <asm/io.h>#include <asm/bugs.h>#include <asm/setup.h>#include <asm/sections.h>#include <asm/cacheflush.h> #define CREATE_TRACE_POINTS#include <trace/events/initcall.h> static int kernel_init(void *); extern void init_IRQ(void);extern void radix_tree_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 extern void time_init(void);/* 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;/* 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 bool initargs_found;#else# define bootconfig_found false# define initargs_found false#endif static char *execute_command;static char *ramdisk_execute_command = "/init"; /* * 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; extern const struct obs_kernel_param __setup_start[], __setup_end[]; 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(u32 *_size, u32 *_csum){	u32 size, csum;	char *data;	u32 *hdr; 	if (!initrd_end)		return NULL; 	data = (char *)initrd_end - BOOTCONFIG_MAGIC_LEN;	if (memcmp(data, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN))		return NULL; 	hdr = (u32 *)(data - 8);	size = hdr[0];	csum = hdr[1]; 	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;	} 	/* Remove bootconfig from initramfs/initrd */	initrd_end = (unsigned long)data;	if (_size)		*_size = size;	if (_csum)		*_csum = csum; 	return data;}#elsestatic void * __init get_boot_config_from_initrd(u32 *_size, u32 *_csum){	return NULL;}#endif #ifdef CONFIG_BOOT_CONFIG char xbc_namebuf[XBC_KEYLEN_MAX] __initdata; #define rest(dst, end) ((end) > (dst) ? (end) - (dst) : 0) static int __init xbc_snprint_cmdline(char *buf, size_t size,				      struct xbc_node *root){	struct xbc_node *knode, *vnode;	char *end = buf + size;	const char *val;	int ret; 	xbc_node_for_each_key_value(root, knode, val) {		ret = xbc_node_compose_key_after(root, knode,					xbc_namebuf, XBC_KEYLEN_MAX);		if (ret < 0)			return ret; 		vnode = xbc_node_get_child(knode);		if (!vnode) {			ret = snprintf(buf, rest(buf, end), "%s ", xbc_namebuf);			if (ret < 0)				return ret;			buf += ret;			continue;		}		xbc_array_for_each_value(vnode, val) {			ret = snprintf(buf, rest(buf, end), "%s=\"%s\" ",				       xbc_namebuf, val);			if (ret < 0)				return ret;			buf += ret;		}	} 	return buf - (end - size);}#undef rest /* 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");		return NULL;	} 	return new_cmdline;} static u32 boot_config_checksum(unsigned char *p, u32 size){	u32 ret = 0; 	while (size--)		ret += *p++; 	return ret;} static int __init bootconfig_params(char *param, char *val,				    const char *unused, void *arg){	if (strcmp(param, "bootconfig") == 0) {		bootconfig_found = true;	}	return 0;} static void __init setup_boot_config(const char *cmdline){	static char tmp_cmdline[COMMAND_LINE_SIZE] __initdata;	const char *msg;	int pos;	u32 size, csum;	char *data, *copy, *err;	int ret; 	/* Cut out the bootconfig data even if we have no bootconfig option */	data = get_boot_config_from_initrd(&size, &csum); 	strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);	err = parse_args("bootconfig", tmp_cmdline, NULL, 0, 0, 0, NULL,			 bootconfig_params); 	if (IS_ERR(err) || !bootconfig_found)		return; 	/* parse_args() stops at '--' and returns an address */	if (err)		initargs_found = true; 	if (!data) {		pr_err("'bootconfig' found on command line, but no bootconfig found\n");		return;	} 	if (size >= XBC_DATA_MAX) {		pr_err("bootconfig size %d greater than max size %d\n",			size, XBC_DATA_MAX);		return;	} 	if (boot_config_checksum((unsigned char *)data, size) != csum) {		pr_err("bootconfig checksum failed\n");		return;	} 	copy = memblock_alloc(size + 1, SMP_CACHE_BYTES);	if (!copy) {		pr_err("Failed to allocate memory for bootconfig\n");		return;	} 	memcpy(copy, data, size);	copy[size] = '\0'; 	ret = xbc_init(copy, &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 {		pr_info("Load bootconfig: %d bytes %d nodes\n", size, ret);		/* keys starting with "kernel." are passed via cmdline */		extra_command_line = xbc_make_cmdline("kernel");		/* Also, "init." keys are init arguments */		extra_init_args = xbc_make_cmdline("init");	}	return;} #else static void __init setup_boot_config(const char *cmdline){	/* Remove bootconfig data from initrd */	get_boot_config_from_initrd(NULL, NULL);} static int __init warn_bootconfig(char *str){	pr_warn("WARNING: 'bootconfig' found on the kernel command line but CONFIG_BOOTCONFIG is not set.\n");	return 0;}early_param("bootconfig", warn_bootconfig); #endif /* 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; 

Lines 1–500 of 1535.