Blame view

kernel/reboot.c 9.23 KB
15d94b825   Robin Holt   reboot: move shut...
1
2
3
4
5
  /*
   *  linux/kernel/reboot.c
   *
   *  Copyright (C) 2013  Linus Torvalds
   */
972ee83df   Robin Holt   reboot: checkpatc...
6
  #define pr_fmt(fmt)	"reboot: " fmt
1b3a5d02e   Robin Holt   reboot: move arch...
7
  #include <linux/ctype.h>
15d94b825   Robin Holt   reboot: move shut...
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  #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);
1b3a5d02e   Robin Holt   reboot: move arch...
25
26
27
28
29
30
31
32
33
34
35
  #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;
15d94b825   Robin Holt   reboot: move shut...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
  /*
   * 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 */
1b3a5d02e   Robin Holt   reboot: move arch...
99
  	int cpu = reboot_cpu;
15d94b825   Robin Holt   reboot: move shut...
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  
  	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)
972ee83df   Robin Holt   reboot: checkpatc...
128
129
  		pr_emerg("Restarting system
  ");
15d94b825   Robin Holt   reboot: move shut...
130
  	else
972ee83df   Robin Holt   reboot: checkpatc...
131
132
  		pr_emerg("Restarting system with command '%s'
  ", cmd);
15d94b825   Robin Holt   reboot: move shut...
133
134
135
136
137
138
139
140
  	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,
972ee83df   Robin Holt   reboot: checkpatc...
141
  		(state == SYSTEM_HALT) ? SYS_HALT : SYS_POWER_OFF, NULL);
15d94b825   Robin Holt   reboot: move shut...
142
143
144
145
146
147
148
149
150
151
152
153
154
155
  	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();
972ee83df   Robin Holt   reboot: checkpatc...
156
157
  	pr_emerg("System halted
  ");
15d94b825   Robin Holt   reboot: move shut...
158
159
160
  	kmsg_dump(KMSG_DUMP_HALT);
  	machine_halt();
  }
15d94b825   Robin Holt   reboot: move shut...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
  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();
972ee83df   Robin Holt   reboot: checkpatc...
175
176
  	pr_emerg("Power down
  ");
15d94b825   Robin Holt   reboot: move shut...
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
  	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 ||
972ee83df   Robin Holt   reboot: checkpatc...
205
206
  			(magic2 != LINUX_REBOOT_MAGIC2 &&
  			magic2 != LINUX_REBOOT_MAGIC2A &&
15d94b825   Robin Holt   reboot: move shut...
207
  			magic2 != LINUX_REBOOT_MAGIC2B &&
972ee83df   Robin Holt   reboot: checkpatc...
208
  			magic2 != LINUX_REBOOT_MAGIC2C))
15d94b825   Robin Holt   reboot: move shut...
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
  		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:
972ee83df   Robin Holt   reboot: checkpatc...
251
252
  		ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);
  		if (ret < 0) {
15d94b825   Robin Holt   reboot: move shut...
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
  			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 {
15d94b825   Robin Holt   reboot: move shut...
318
319
320
321
  		ret = -ENOMEM;
  	}
  
  	if (ret && force) {
972ee83df   Robin Holt   reboot: checkpatc...
322
323
  		pr_warn("Failed to start orderly shutdown: forcing the issue
  ");
15d94b825   Robin Holt   reboot: move shut...
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
  		/*
  		 * 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);
1b3a5d02e   Robin Holt   reboot: move arch...
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
  
  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);