diff options
Diffstat (limited to 'init/main.c')
| -rw-r--r-- | init/main.c | 203 | 
1 files changed, 162 insertions, 41 deletions
diff --git a/init/main.c b/init/main.c index af310afbef2..e8ae1fef090 100644 --- a/init/main.c +++ b/init/main.c @@ -76,6 +76,8 @@  #include <linux/elevator.h>  #include <linux/sched_clock.h>  #include <linux/context_tracking.h> +#include <linux/random.h> +#include <linux/list.h>  #include <asm/io.h>  #include <asm/bugs.h> @@ -91,17 +93,11 @@ static int kernel_init(void *);  extern void init_IRQ(void);  extern void fork_init(unsigned long); -extern void mca_init(void); -extern void sbus_init(void);  extern void radix_tree_init(void);  #ifndef CONFIG_DEBUG_RODATA  static inline void mark_rodata_ro(void) { }  #endif -#ifdef CONFIG_TC -extern void tc_init(void); -#endif -  /*   * 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 @@ -123,7 +119,6 @@ EXPORT_SYMBOL(system_state);  extern void time_init(void);  /* Default late time init is NULL. archs can override this later. */  void (*__initdata late_time_init)(void); -extern void softirq_init(void);  /* Untouched command line saved by arch-specific code. */  char __initdata boot_command_line[COMMAND_LINE_SIZE]; @@ -131,11 +126,20 @@ char __initdata boot_command_line[COMMAND_LINE_SIZE];  char *saved_command_line;  /* Command line for parameter parsing */  static char *static_command_line; +/* Command line for per-initcall parameter parsing */ +static char *initcall_command_line;  static char *execute_command;  static char *ramdisk_execute_command;  /* + * Used to generate warnings if static_key manipulation functions are used + * before jump_label_init is called. + */ +bool static_key_initialized __read_mostly = false; +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. @@ -200,13 +204,13 @@ EXPORT_SYMBOL(loops_per_jiffy);  static int __init debug_kernel(char *str)  { -	console_loglevel = 10; +	console_loglevel = CONSOLE_LOGLEVEL_DEBUG;  	return 0;  }  static int __init quiet_kernel(char *str)  { -	console_loglevel = 4; +	console_loglevel = CONSOLE_LOGLEVEL_QUIET;  	return 0;  } @@ -249,6 +253,27 @@ static int __init repair_env_string(char *param, char *val, const char *unused)  	return 0;  } +/* Anything after -- gets handed straight to init. */ +static int __init set_init_arg(char *param, char *val, const char *unused) +{ +	unsigned int i; + +	if (panic_later) +		return 0; + +	repair_env_string(param, val, unused); + +	for (i = 0; argv_init[i]; i++) { +		if (i == MAX_INIT_ARGS) { +			panic_later = "init"; +			panic_param = param; +			return 0; +		} +	} +	argv_init[i] = param; +	return 0; +} +  /*   * Unknown boot options get handed to init, unless they look like   * unused parameters (modprobe will find them in /proc/cmdline). @@ -273,7 +298,7 @@ static int __init unknown_bootoption(char *param, char *val, const char *unused)  		unsigned int i;  		for (i = 0; envp_init[i]; i++) {  			if (i == MAX_INIT_ENVS) { -				panic_later = "Too many boot env vars at `%s'"; +				panic_later = "env";  				panic_param = param;  			}  			if (!strncmp(param, envp_init[i], val - param)) @@ -285,7 +310,7 @@ static int __init unknown_bootoption(char *param, char *val, const char *unused)  		unsigned int i;  		for (i = 0; argv_init[i]; i++) {  			if (i == MAX_INIT_ARGS) { -				panic_later = "Too many boot init vars at `%s'"; +				panic_later = "init";  				panic_param = param;  			}  		} @@ -346,8 +371,11 @@ static inline void smp_prepare_cpus(unsigned int maxcpus) { }   */  static void __init setup_command_line(char *command_line)  { -	saved_command_line = alloc_bootmem(strlen (boot_command_line)+1); -	static_command_line = alloc_bootmem(strlen (command_line)+1); +	saved_command_line = +		memblock_virt_alloc(strlen(boot_command_line) + 1, 0); +	initcall_command_line = +		memblock_virt_alloc(strlen(boot_command_line) + 1, 0); +	static_command_line = memblock_virt_alloc(strlen(command_line) + 1, 0);  	strcpy (saved_command_line, boot_command_line);  	strcpy (static_command_line, command_line);  } @@ -373,7 +401,7 @@ static noinline void __init_refok rest_init(void)  	 * the init task will end up wanting to create kthreads, which, if  	 * we schedule it before we create kthreadd, will OOPS.  	 */ -	kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); +	kernel_thread(kernel_init, NULL, CLONE_FS);  	numa_default_policy();  	pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);  	rcu_read_lock(); @@ -466,13 +494,13 @@ static void __init mm_init(void)  	mem_init();  	kmem_cache_init();  	percpu_init_late(); -	pgtable_cache_init(); +	pgtable_init();  	vmalloc_init();  } -asmlinkage void __init start_kernel(void) +asmlinkage __visible void __init start_kernel(void)  { -	char * command_line; +	char * command_line, *after_dashes;  	extern const struct kernel_param __start___param[], __stop___param[];  	/* @@ -501,7 +529,6 @@ asmlinkage void __init start_kernel(void)  	page_address_init();  	pr_notice("%s", linux_banner);  	setup_arch(&command_line); -	mm_init_owner(&init_mm, &init_task);  	mm_init_cpumask(&init_mm);  	setup_command_line(command_line);  	setup_nr_cpu_ids(); @@ -513,9 +540,13 @@ asmlinkage void __init start_kernel(void)  	pr_notice("Kernel command line: %s\n", boot_command_line);  	parse_early_param(); -	parse_args("Booting kernel", static_command_line, __start___param, -		   __stop___param - __start___param, -		   -1, -1, &unknown_bootoption); +	after_dashes = parse_args("Booting kernel", +				  static_command_line, __start___param, +				  __stop___param - __start___param, +				  -1, -1, &unknown_bootoption); +	if (after_dashes) +		parse_args("Setting init args", after_dashes, NULL, 0, -1, -1, +			   set_init_arg);  	jump_label_init(); @@ -574,7 +605,8 @@ asmlinkage void __init start_kernel(void)  	 */  	console_init();  	if (panic_later) -		panic(panic_later, panic_param); +		panic("Too many boot %s vars at `%s'", panic_later, +		      panic_param);  	lockdep_info(); @@ -605,10 +637,15 @@ asmlinkage void __init start_kernel(void)  	calibrate_delay();  	pidmap_init();  	anon_vma_init(); +	acpi_early_init();  #ifdef CONFIG_X86  	if (efi_enabled(EFI_RUNTIME_SERVICES))  		efi_enter_virtual_mode();  #endif +#ifdef CONFIG_X86_ESPFIX64 +	/* Should be run before the first non-init thread is created */ +	init_espfix_bsp(); +#endif  	thread_info_cache_init();  	cred_init();  	fork_init(totalram_pages); @@ -621,9 +658,7 @@ asmlinkage void __init start_kernel(void)  	signals_init();  	/* rootfs populating might need page-writeback */  	page_writeback_init(); -#ifdef CONFIG_PROC_FS  	proc_root_init(); -#endif  	cgroup_init();  	cpuset_init();  	taskstats_init_early(); @@ -631,7 +666,6 @@ asmlinkage void __init start_kernel(void)  	check_bugs(); -	acpi_early_init(); /* before LAPIC and SMP init */  	sfi_init_late();  	if (efi_enabled(EFI_RUNTIME_SERVICES)) { @@ -659,19 +693,83 @@ static void __init do_ctors(void)  bool initcall_debug;  core_param(initcall_debug, initcall_debug, bool, 0644); +#ifdef CONFIG_KALLSYMS +struct blacklist_entry { +	struct list_head next; +	char *buf; +}; + +static __initdata_or_module LIST_HEAD(blacklisted_initcalls); + +static int __init initcall_blacklist(char *str) +{ +	char *str_entry; +	struct blacklist_entry *entry; + +	/* str argument is a comma-separated list of functions */ +	do { +		str_entry = strsep(&str, ","); +		if (str_entry) { +			pr_debug("blacklisting initcall %s\n", str_entry); +			entry = alloc_bootmem(sizeof(*entry)); +			entry->buf = alloc_bootmem(strlen(str_entry) + 1); +			strcpy(entry->buf, str_entry); +			list_add(&entry->next, &blacklisted_initcalls); +		} +	} while (str_entry); + +	return 0; +} + +static bool __init_or_module initcall_blacklisted(initcall_t fn) +{ +	struct list_head *tmp; +	struct blacklist_entry *entry; +	char *fn_name; + +	fn_name = kasprintf(GFP_KERNEL, "%pf", fn); +	if (!fn_name) +		return false; + +	list_for_each(tmp, &blacklisted_initcalls) { +		entry = list_entry(tmp, struct blacklist_entry, next); +		if (!strcmp(fn_name, entry->buf)) { +			pr_debug("initcall %s blacklisted\n", fn_name); +			kfree(fn_name); +			return true; +		} +	} + +	kfree(fn_name); +	return false; +} +#else +static int __init initcall_blacklist(char *str) +{ +	pr_warn("initcall_blacklist requires CONFIG_KALLSYMS\n"); +	return 0; +} + +static bool __init_or_module initcall_blacklisted(initcall_t fn) +{ +	return false; +} +#endif +__setup("initcall_blacklist=", initcall_blacklist); +  static int __init_or_module do_one_initcall_debug(initcall_t fn)  {  	ktime_t calltime, delta, rettime;  	unsigned long long duration;  	int ret; -	pr_debug("calling  %pF @ %i\n", fn, task_pid_nr(current)); +	printk(KERN_DEBUG "calling  %pF @ %i\n", fn, task_pid_nr(current));  	calltime = ktime_get();  	ret = fn();  	rettime = ktime_get();  	delta = ktime_sub(rettime, calltime);  	duration = (unsigned long long) ktime_to_ns(delta) >> 10; -	pr_debug("initcall %pF returned %d after %lld usecs\n", +	printk(KERN_DEBUG "initcall %pF returned %d after %lld usecs\n",  		 fn, ret, duration);  	return ret; @@ -683,6 +781,9 @@ int __init_or_module do_one_initcall(initcall_t fn)  	int ret;  	char msgbuf[64]; +	if (initcall_blacklisted(fn)) +		return -EPERM; +  	if (initcall_debug)  		ret = do_one_initcall_debug(fn);  	else @@ -692,7 +793,7 @@ int __init_or_module do_one_initcall(initcall_t fn)  	if (preempt_count() != count) {  		sprintf(msgbuf, "preemption imbalance "); -		preempt_count() = count; +		preempt_count_set(count);  	}  	if (irqs_disabled()) {  		strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf)); @@ -744,9 +845,9 @@ static void __init do_initcall_level(int level)  	extern const struct kernel_param __start___param[], __stop___param[];  	initcall_t *fn; -	strcpy(static_command_line, saved_command_line); +	strcpy(initcall_command_line, saved_command_line);  	parse_args(initcall_level_names[level], -		   static_command_line, __start___param, +		   initcall_command_line, __start___param,  		   __stop___param - __start___param,  		   level, level,  		   &repair_env_string); @@ -780,6 +881,7 @@ static void __init do_basic_setup(void)  	do_ctors();  	usermodehelper_enable();  	do_initcalls(); +	random_int_secret_init();  }  static void __init do_pre_smp_initcalls(void) @@ -804,15 +906,31 @@ void __init load_default_modules(void)  static int run_init_process(const char *init_filename)  {  	argv_init[0] = init_filename; -	return do_execve(init_filename, +	return do_execve(getname_kernel(init_filename),  		(const char __user *const __user *)argv_init,  		(const char __user *const __user *)envp_init);  } +static int try_to_run_init_process(const char *init_filename) +{ +	int ret; + +	ret = run_init_process(init_filename); + +	if (ret && ret != -ENOENT) { +		pr_err("Starting init: %s exists but couldn't execute it (error %d)\n", +		       init_filename, ret); +	} + +	return ret; +} +  static noinline void __init kernel_init_freeable(void);  static int __ref kernel_init(void *unused)  { +	int ret; +  	kernel_init_freeable();  	/* need to finish all async __init code before freeing the memory */  	async_synchronize_full(); @@ -824,9 +942,11 @@ static int __ref kernel_init(void *unused)  	flush_delayed_fput();  	if (ramdisk_execute_command) { -		if (!run_init_process(ramdisk_execute_command)) +		ret = run_init_process(ramdisk_execute_command); +		if (!ret)  			return 0; -		pr_err("Failed to execute %s\n", ramdisk_execute_command); +		pr_err("Failed to execute %s (error %d)\n", +		       ramdisk_execute_command, ret);  	}  	/* @@ -836,18 +956,19 @@ static int __ref kernel_init(void *unused)  	 * trying to recover a really broken machine.  	 */  	if (execute_command) { -		if (!run_init_process(execute_command)) +		ret = run_init_process(execute_command); +		if (!ret)  			return 0; -		pr_err("Failed to execute %s.  Attempting defaults...\n", -			execute_command); +		pr_err("Failed to execute %s (error %d).  Attempting defaults...\n", +			execute_command, ret);  	} -	if (!run_init_process("/sbin/init") || -	    !run_init_process("/etc/init") || -	    !run_init_process("/bin/init") || -	    !run_init_process("/bin/sh")) +	if (!try_to_run_init_process("/sbin/init") || +	    !try_to_run_init_process("/etc/init") || +	    !try_to_run_init_process("/bin/init") || +	    !try_to_run_init_process("/bin/sh"))  		return 0; -	panic("No init found.  Try passing init= option to kernel. " +	panic("No working init found.  Try passing init= option to kernel. "  	      "See Linux Documentation/init.txt for guidance.");  }  | 
