diff options
Diffstat (limited to 'kernel/reboot.c')
| -rw-r--r-- | kernel/reboot.c | 419 | 
1 files changed, 419 insertions, 0 deletions
| diff --git a/kernel/reboot.c b/kernel/reboot.c new file mode 100644 index 00000000000..269ed9384cc --- /dev/null +++ b/kernel/reboot.c @@ -0,0 +1,419 @@ +/* + *  linux/kernel/reboot.c + * + *  Copyright (C) 2013  Linus Torvalds + */ + +#define pr_fmt(fmt)	"reboot: " fmt + +#include <linux/ctype.h> +#include <linux/export.h> +#include <linux/kexec.h> +#include <linux/kmod.h> +#include <linux/kmsg_dump.h> +#include <linux/reboot.h> +#include <linux/suspend.h> +#include <linux/syscalls.h> +#include <linux/syscore_ops.h> +#include <linux/uaccess.h> + +/* + * this indicates whether you can reboot with ctrl-alt-del: the default is yes + */ + +int C_A_D = 1; +struct pid *cad_pid; +EXPORT_SYMBOL(cad_pid); + +#if defined(CONFIG_ARM) || defined(CONFIG_UNICORE32) +#define DEFAULT_REBOOT_MODE		= REBOOT_HARD +#else +#define DEFAULT_REBOOT_MODE +#endif +enum reboot_mode reboot_mode DEFAULT_REBOOT_MODE; + +int reboot_default; +int reboot_cpu; +enum reboot_type reboot_type = BOOT_ACPI; +int reboot_force; + +/* + * If set, this is used for preparing the system to power off. + */ + +void (*pm_power_off_prepare)(void); + +/** + *	emergency_restart - reboot the system + * + *	Without shutting down any hardware or taking any locks + *	reboot the system.  This is called when we know we are in + *	trouble so this is our best effort to reboot.  This is + *	safe to call in interrupt context. + */ +void emergency_restart(void) +{ +	kmsg_dump(KMSG_DUMP_EMERG); +	machine_emergency_restart(); +} +EXPORT_SYMBOL_GPL(emergency_restart); + +void kernel_restart_prepare(char *cmd) +{ +	blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); +	system_state = SYSTEM_RESTART; +	usermodehelper_disable(); +	device_shutdown(); +} + +/** + *	register_reboot_notifier - Register function to be called at reboot time + *	@nb: Info about notifier function to be called + * + *	Registers a function with the list of functions + *	to be called at reboot time. + * + *	Currently always returns zero, as blocking_notifier_chain_register() + *	always returns zero. + */ +int register_reboot_notifier(struct notifier_block *nb) +{ +	return blocking_notifier_chain_register(&reboot_notifier_list, nb); +} +EXPORT_SYMBOL(register_reboot_notifier); + +/** + *	unregister_reboot_notifier - Unregister previously registered reboot notifier + *	@nb: Hook to be unregistered + * + *	Unregisters a previously registered reboot + *	notifier function. + * + *	Returns zero on success, or %-ENOENT on failure. + */ +int unregister_reboot_notifier(struct notifier_block *nb) +{ +	return blocking_notifier_chain_unregister(&reboot_notifier_list, nb); +} +EXPORT_SYMBOL(unregister_reboot_notifier); + +static void migrate_to_reboot_cpu(void) +{ +	/* The boot cpu is always logical cpu 0 */ +	int cpu = reboot_cpu; + +	cpu_hotplug_disable(); + +	/* Make certain the cpu I'm about to reboot on is online */ +	if (!cpu_online(cpu)) +		cpu = cpumask_first(cpu_online_mask); + +	/* Prevent races with other tasks migrating this task */ +	current->flags |= PF_NO_SETAFFINITY; + +	/* Make certain I only run on the appropriate processor */ +	set_cpus_allowed_ptr(current, cpumask_of(cpu)); +} + +/** + *	kernel_restart - reboot the system + *	@cmd: pointer to buffer containing command to execute for restart + *		or %NULL + * + *	Shutdown everything and perform a clean reboot. + *	This is not safe to call in interrupt context. + */ +void kernel_restart(char *cmd) +{ +	kernel_restart_prepare(cmd); +	migrate_to_reboot_cpu(); +	syscore_shutdown(); +	if (!cmd) +		pr_emerg("Restarting system\n"); +	else +		pr_emerg("Restarting system with command '%s'\n", cmd); +	kmsg_dump(KMSG_DUMP_RESTART); +	machine_restart(cmd); +} +EXPORT_SYMBOL_GPL(kernel_restart); + +static void kernel_shutdown_prepare(enum system_states state) +{ +	blocking_notifier_call_chain(&reboot_notifier_list, +		(state == SYSTEM_HALT) ? SYS_HALT : SYS_POWER_OFF, NULL); +	system_state = state; +	usermodehelper_disable(); +	device_shutdown(); +} +/** + *	kernel_halt - halt the system + * + *	Shutdown everything and perform a clean system halt. + */ +void kernel_halt(void) +{ +	kernel_shutdown_prepare(SYSTEM_HALT); +	migrate_to_reboot_cpu(); +	syscore_shutdown(); +	pr_emerg("System halted\n"); +	kmsg_dump(KMSG_DUMP_HALT); +	machine_halt(); +} +EXPORT_SYMBOL_GPL(kernel_halt); + +/** + *	kernel_power_off - power_off the system + * + *	Shutdown everything and perform a clean system power_off. + */ +void kernel_power_off(void) +{ +	kernel_shutdown_prepare(SYSTEM_POWER_OFF); +	if (pm_power_off_prepare) +		pm_power_off_prepare(); +	migrate_to_reboot_cpu(); +	syscore_shutdown(); +	pr_emerg("Power down\n"); +	kmsg_dump(KMSG_DUMP_POWEROFF); +	machine_power_off(); +} +EXPORT_SYMBOL_GPL(kernel_power_off); + +static DEFINE_MUTEX(reboot_mutex); + +/* + * Reboot system call: for obvious reasons only root may call it, + * and even root needs to set up some magic numbers in the registers + * so that some mistake won't make this reboot the whole machine. + * You can also set the meaning of the ctrl-alt-del-key here. + * + * reboot doesn't sync: do that yourself before calling this. + */ +SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, +		void __user *, arg) +{ +	struct pid_namespace *pid_ns = task_active_pid_ns(current); +	char buffer[256]; +	int ret = 0; + +	/* We only trust the superuser with rebooting the system. */ +	if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT)) +		return -EPERM; + +	/* For safety, we require "magic" arguments. */ +	if (magic1 != LINUX_REBOOT_MAGIC1 || +			(magic2 != LINUX_REBOOT_MAGIC2 && +			magic2 != LINUX_REBOOT_MAGIC2A && +			magic2 != LINUX_REBOOT_MAGIC2B && +			magic2 != LINUX_REBOOT_MAGIC2C)) +		return -EINVAL; + +	/* +	 * If pid namespaces are enabled and the current task is in a child +	 * pid_namespace, the command is handled by reboot_pid_ns() which will +	 * call do_exit(). +	 */ +	ret = reboot_pid_ns(pid_ns, cmd); +	if (ret) +		return ret; + +	/* Instead of trying to make the power_off code look like +	 * halt when pm_power_off is not set do it the easy way. +	 */ +	if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off) +		cmd = LINUX_REBOOT_CMD_HALT; + +	mutex_lock(&reboot_mutex); +	switch (cmd) { +	case LINUX_REBOOT_CMD_RESTART: +		kernel_restart(NULL); +		break; + +	case LINUX_REBOOT_CMD_CAD_ON: +		C_A_D = 1; +		break; + +	case LINUX_REBOOT_CMD_CAD_OFF: +		C_A_D = 0; +		break; + +	case LINUX_REBOOT_CMD_HALT: +		kernel_halt(); +		do_exit(0); +		panic("cannot halt"); + +	case LINUX_REBOOT_CMD_POWER_OFF: +		kernel_power_off(); +		do_exit(0); +		break; + +	case LINUX_REBOOT_CMD_RESTART2: +		ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1); +		if (ret < 0) { +			ret = -EFAULT; +			break; +		} +		buffer[sizeof(buffer) - 1] = '\0'; + +		kernel_restart(buffer); +		break; + +#ifdef CONFIG_KEXEC +	case LINUX_REBOOT_CMD_KEXEC: +		ret = kernel_kexec(); +		break; +#endif + +#ifdef CONFIG_HIBERNATION +	case LINUX_REBOOT_CMD_SW_SUSPEND: +		ret = hibernate(); +		break; +#endif + +	default: +		ret = -EINVAL; +		break; +	} +	mutex_unlock(&reboot_mutex); +	return ret; +} + +static void deferred_cad(struct work_struct *dummy) +{ +	kernel_restart(NULL); +} + +/* + * This function gets called by ctrl-alt-del - ie the keyboard interrupt. + * As it's called within an interrupt, it may NOT sync: the only choice + * is whether to reboot at once, or just ignore the ctrl-alt-del. + */ +void ctrl_alt_del(void) +{ +	static DECLARE_WORK(cad_work, deferred_cad); + +	if (C_A_D) +		schedule_work(&cad_work); +	else +		kill_cad_pid(SIGINT, 1); +} + +char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff"; + +static int __orderly_poweroff(bool force) +{ +	char **argv; +	static char *envp[] = { +		"HOME=/", +		"PATH=/sbin:/bin:/usr/sbin:/usr/bin", +		NULL +	}; +	int ret; + +	argv = argv_split(GFP_KERNEL, poweroff_cmd, NULL); +	if (argv) { +		ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); +		argv_free(argv); +	} else { +		ret = -ENOMEM; +	} + +	if (ret && force) { +		pr_warn("Failed to start orderly shutdown: forcing the issue\n"); +		/* +		 * I guess this should try to kick off some daemon to sync and +		 * poweroff asap.  Or not even bother syncing if we're doing an +		 * emergency shutdown? +		 */ +		emergency_sync(); +		kernel_power_off(); +	} + +	return ret; +} + +static bool poweroff_force; + +static void poweroff_work_func(struct work_struct *work) +{ +	__orderly_poweroff(poweroff_force); +} + +static DECLARE_WORK(poweroff_work, poweroff_work_func); + +/** + * orderly_poweroff - Trigger an orderly system poweroff + * @force: force poweroff if command execution fails + * + * This may be called from any context to trigger a system shutdown. + * If the orderly shutdown fails, it will force an immediate shutdown. + */ +int orderly_poweroff(bool force) +{ +	if (force) /* do not override the pending "true" */ +		poweroff_force = true; +	schedule_work(&poweroff_work); +	return 0; +} +EXPORT_SYMBOL_GPL(orderly_poweroff); + +static int __init reboot_setup(char *str) +{ +	for (;;) { +		/* +		 * Having anything passed on the command line via +		 * reboot= will cause us to disable DMI checking +		 * below. +		 */ +		reboot_default = 0; + +		switch (*str) { +		case 'w': +			reboot_mode = REBOOT_WARM; +			break; + +		case 'c': +			reboot_mode = REBOOT_COLD; +			break; + +		case 'h': +			reboot_mode = REBOOT_HARD; +			break; + +		case 's': +			if (isdigit(*(str+1))) +				reboot_cpu = simple_strtoul(str+1, NULL, 0); +			else if (str[1] == 'm' && str[2] == 'p' && +							isdigit(*(str+3))) +				reboot_cpu = simple_strtoul(str+3, NULL, 0); +			else +				reboot_mode = REBOOT_SOFT; +			break; + +		case 'g': +			reboot_mode = REBOOT_GPIO; +			break; + +		case 'b': +		case 'a': +		case 'k': +		case 't': +		case 'e': +		case 'p': +			reboot_type = *str; +			break; + +		case 'f': +			reboot_force = 1; +			break; +		} + +		str = strchr(str, ','); +		if (str) +			str++; +		else +			break; +	} +	return 1; +} +__setup("reboot=", reboot_setup); | 
