xref: /openbmc/linux/kernel/power/hibernate.c (revision 060f35a317ef09101b128f399dce7ed13d019461)
155716d26SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28b759b84SRafael J. Wysocki /*
38b759b84SRafael J. Wysocki  * kernel/power/hibernate.c - Hibernation (a.k.a suspend-to-disk) support.
48b759b84SRafael J. Wysocki  *
58b759b84SRafael J. Wysocki  * Copyright (c) 2003 Patrick Mochel
68b759b84SRafael J. Wysocki  * Copyright (c) 2003 Open Source Development Lab
7a2531293SPavel Machek  * Copyright (c) 2004 Pavel Machek <pavel@ucw.cz>
88b759b84SRafael J. Wysocki  * Copyright (c) 2009 Rafael J. Wysocki, Novell Inc.
962c552ccSBojan Smojver  * Copyright (C) 2012 Bojan Smojver <bojan@rexursive.com>
108b759b84SRafael J. Wysocki  */
118b759b84SRafael J. Wysocki 
127a7b99bfSLuigi Semenzato #define pr_fmt(fmt) "PM: hibernation: " fmt
132872de13SRafael J. Wysocki 
14cf056a43SChristoph Hellwig #include <linux/blkdev.h>
156e5fdeedSPaul Gortmaker #include <linux/export.h>
168b759b84SRafael J. Wysocki #include <linux/suspend.h>
178b759b84SRafael J. Wysocki #include <linux/reboot.h>
188b759b84SRafael J. Wysocki #include <linux/string.h>
198b759b84SRafael J. Wysocki #include <linux/device.h>
206f8d7022SBarry Song #include <linux/async.h>
218b759b84SRafael J. Wysocki #include <linux/delay.h>
228b759b84SRafael J. Wysocki #include <linux/fs.h>
238b759b84SRafael J. Wysocki #include <linux/mount.h>
248b759b84SRafael J. Wysocki #include <linux/pm.h>
2538b8d208SIngo Molnar #include <linux/nmi.h>
268b759b84SRafael J. Wysocki #include <linux/console.h>
278b759b84SRafael J. Wysocki #include <linux/cpu.h>
288b759b84SRafael J. Wysocki #include <linux/freezer.h>
295a0e3ad6STejun Heo #include <linux/gfp.h>
3040dc166cSRafael J. Wysocki #include <linux/syscore_ops.h>
312df83fa4SMinho Ban #include <linux/ctype.h>
32db597605STina Ruchandani #include <linux/ktime.h>
3338bd94b8SJosh Boyer #include <linux/security.h>
349a436f8fSMike Rapoport #include <linux/secretmem.h>
35bb3632c6STodd E Brandt #include <trace/events/power.h>
368b759b84SRafael J. Wysocki 
378b759b84SRafael J. Wysocki #include "power.h"
388b759b84SRafael J. Wysocki 
398b759b84SRafael J. Wysocki 
40d231ff1aSBarry Song static int nocompress;
41d231ff1aSBarry Song static int noresume;
42a6e15a39SKees Cook static int nohibernate;
43d231ff1aSBarry Song static int resume_wait;
44f6514be5SDan Carpenter static unsigned int resume_delay;
458b759b84SRafael J. Wysocki static char resume_file[256] = CONFIG_PM_STD_PARTITION;
468b759b84SRafael J. Wysocki dev_t swsusp_resume_device;
478b759b84SRafael J. Wysocki sector_t swsusp_resume_block;
48d6efc2f7SAndi Kleen __visible int in_suspend __nosavedata;
498b759b84SRafael J. Wysocki 
508b759b84SRafael J. Wysocki enum {
518b759b84SRafael J. Wysocki 	HIBERNATION_INVALID,
528b759b84SRafael J. Wysocki 	HIBERNATION_PLATFORM,
538b759b84SRafael J. Wysocki 	HIBERNATION_SHUTDOWN,
548b759b84SRafael J. Wysocki 	HIBERNATION_REBOOT,
5562c552ccSBojan Smojver #ifdef CONFIG_SUSPEND
5662c552ccSBojan Smojver 	HIBERNATION_SUSPEND,
5762c552ccSBojan Smojver #endif
58fe12c00dSChen Yu 	HIBERNATION_TEST_RESUME,
598b759b84SRafael J. Wysocki 	/* keep last */
608b759b84SRafael J. Wysocki 	__HIBERNATION_AFTER_LAST
618b759b84SRafael J. Wysocki };
628b759b84SRafael J. Wysocki #define HIBERNATION_MAX (__HIBERNATION_AFTER_LAST-1)
638b759b84SRafael J. Wysocki #define HIBERNATION_FIRST (HIBERNATION_INVALID + 1)
648b759b84SRafael J. Wysocki 
658b759b84SRafael J. Wysocki static int hibernation_mode = HIBERNATION_SHUTDOWN;
668b759b84SRafael J. Wysocki 
6797819a26SSrivatsa S. Bhat bool freezer_test_done;
68aa9a7b11SSrivatsa S. Bhat 
69073ef1f6SLionel Debroux static const struct platform_hibernation_ops *hibernation_ops;
708b759b84SRafael J. Wysocki 
71ab7e9b06SDomenico Andreoli static atomic_t hibernate_atomic = ATOMIC_INIT(1);
72ab7e9b06SDomenico Andreoli 
hibernate_acquire(void)73ab7e9b06SDomenico Andreoli bool hibernate_acquire(void)
74ab7e9b06SDomenico Andreoli {
75ab7e9b06SDomenico Andreoli 	return atomic_add_unless(&hibernate_atomic, -1, 0);
76ab7e9b06SDomenico Andreoli }
77ab7e9b06SDomenico Andreoli 
hibernate_release(void)78ab7e9b06SDomenico Andreoli void hibernate_release(void)
79ab7e9b06SDomenico Andreoli {
80ab7e9b06SDomenico Andreoli 	atomic_inc(&hibernate_atomic);
81ab7e9b06SDomenico Andreoli }
82ab7e9b06SDomenico Andreoli 
hibernation_available(void)83a6e15a39SKees Cook bool hibernation_available(void)
84a6e15a39SKees Cook {
859a436f8fSMike Rapoport 	return nohibernate == 0 &&
869a436f8fSMike Rapoport 		!security_locked_down(LOCKDOWN_HIBERNATION) &&
879ea4dcf4SDan Williams 		!secretmem_active() && !cxl_mem_active();
88a6e15a39SKees Cook }
89a6e15a39SKees Cook 
908b759b84SRafael J. Wysocki /**
91f42a9813SRafael J. Wysocki  * hibernation_set_ops - Set the global hibernate operations.
92f42a9813SRafael J. Wysocki  * @ops: Hibernation operations to use in subsequent hibernation transitions.
938b759b84SRafael J. Wysocki  */
hibernation_set_ops(const struct platform_hibernation_ops * ops)94073ef1f6SLionel Debroux void hibernation_set_ops(const struct platform_hibernation_ops *ops)
958b759b84SRafael J. Wysocki {
965950e5d5SPeter Zijlstra 	unsigned int sleep_flags;
975950e5d5SPeter Zijlstra 
988b759b84SRafael J. Wysocki 	if (ops && !(ops->begin && ops->end &&  ops->pre_snapshot
998b759b84SRafael J. Wysocki 	    && ops->prepare && ops->finish && ops->enter && ops->pre_restore
1005729c63aSMyungJoo Ham 	    && ops->restore_cleanup && ops->leave)) {
1018b759b84SRafael J. Wysocki 		WARN_ON(1);
1028b759b84SRafael J. Wysocki 		return;
1038b759b84SRafael J. Wysocki 	}
1045950e5d5SPeter Zijlstra 
1055950e5d5SPeter Zijlstra 	sleep_flags = lock_system_sleep();
1065950e5d5SPeter Zijlstra 
1078b759b84SRafael J. Wysocki 	hibernation_ops = ops;
1088b759b84SRafael J. Wysocki 	if (ops)
1098b759b84SRafael J. Wysocki 		hibernation_mode = HIBERNATION_PLATFORM;
1108b759b84SRafael J. Wysocki 	else if (hibernation_mode == HIBERNATION_PLATFORM)
1118b759b84SRafael J. Wysocki 		hibernation_mode = HIBERNATION_SHUTDOWN;
1128b759b84SRafael J. Wysocki 
1135950e5d5SPeter Zijlstra 	unlock_system_sleep(sleep_flags);
1148b759b84SRafael J. Wysocki }
115e0c7855eSLeonardo Potenza EXPORT_SYMBOL_GPL(hibernation_set_ops);
1168b759b84SRafael J. Wysocki 
1178b759b84SRafael J. Wysocki static bool entering_platform_hibernation;
1188b759b84SRafael J. Wysocki 
system_entering_hibernation(void)1198b759b84SRafael J. Wysocki bool system_entering_hibernation(void)
1208b759b84SRafael J. Wysocki {
1218b759b84SRafael J. Wysocki 	return entering_platform_hibernation;
1228b759b84SRafael J. Wysocki }
1238b759b84SRafael J. Wysocki EXPORT_SYMBOL(system_entering_hibernation);
1248b759b84SRafael J. Wysocki 
1258b759b84SRafael J. Wysocki #ifdef CONFIG_PM_DEBUG
hibernation_debug_sleep(void)1268b759b84SRafael J. Wysocki static void hibernation_debug_sleep(void)
1278b759b84SRafael J. Wysocki {
1287a7b99bfSLuigi Semenzato 	pr_info("debug: Waiting for 5 seconds.\n");
1298b759b84SRafael J. Wysocki 	mdelay(5000);
1308b759b84SRafael J. Wysocki }
1318b759b84SRafael J. Wysocki 
hibernation_test(int level)1328b759b84SRafael J. Wysocki static int hibernation_test(int level)
1338b759b84SRafael J. Wysocki {
1348b759b84SRafael J. Wysocki 	if (pm_test_level == level) {
1358b759b84SRafael J. Wysocki 		hibernation_debug_sleep();
1368b759b84SRafael J. Wysocki 		return 1;
1378b759b84SRafael J. Wysocki 	}
1388b759b84SRafael J. Wysocki 	return 0;
1398b759b84SRafael J. Wysocki }
1408b759b84SRafael J. Wysocki #else /* !CONFIG_PM_DEBUG */
hibernation_test(int level)1418b759b84SRafael J. Wysocki static int hibernation_test(int level) { return 0; }
1428b759b84SRafael J. Wysocki #endif /* !CONFIG_PM_DEBUG */
1438b759b84SRafael J. Wysocki 
1448b759b84SRafael J. Wysocki /**
145f42a9813SRafael J. Wysocki  * platform_begin - Call platform to start hibernation.
146f42a9813SRafael J. Wysocki  * @platform_mode: Whether or not to use the platform driver.
1478b759b84SRafael J. Wysocki  */
platform_begin(int platform_mode)1488b759b84SRafael J. Wysocki static int platform_begin(int platform_mode)
1498b759b84SRafael J. Wysocki {
1508b759b84SRafael J. Wysocki 	return (platform_mode && hibernation_ops) ?
151bb186901SRafael J. Wysocki 		hibernation_ops->begin(PMSG_FREEZE) : 0;
1528b759b84SRafael J. Wysocki }
1538b759b84SRafael J. Wysocki 
1548b759b84SRafael J. Wysocki /**
155f42a9813SRafael J. Wysocki  * platform_end - Call platform to finish transition to the working state.
156f42a9813SRafael J. Wysocki  * @platform_mode: Whether or not to use the platform driver.
1578b759b84SRafael J. Wysocki  */
platform_end(int platform_mode)1588b759b84SRafael J. Wysocki static void platform_end(int platform_mode)
1598b759b84SRafael J. Wysocki {
1608b759b84SRafael J. Wysocki 	if (platform_mode && hibernation_ops)
1618b759b84SRafael J. Wysocki 		hibernation_ops->end();
1628b759b84SRafael J. Wysocki }
1638b759b84SRafael J. Wysocki 
1648b759b84SRafael J. Wysocki /**
165f42a9813SRafael J. Wysocki  * platform_pre_snapshot - Call platform to prepare the machine for hibernation.
166f42a9813SRafael J. Wysocki  * @platform_mode: Whether or not to use the platform driver.
167f42a9813SRafael J. Wysocki  *
168f42a9813SRafael J. Wysocki  * Use the platform driver to prepare the system for creating a hibernate image,
169f42a9813SRafael J. Wysocki  * if so configured, and return an error code if that fails.
1708b759b84SRafael J. Wysocki  */
1718b759b84SRafael J. Wysocki 
platform_pre_snapshot(int platform_mode)1728b759b84SRafael J. Wysocki static int platform_pre_snapshot(int platform_mode)
1738b759b84SRafael J. Wysocki {
1748b759b84SRafael J. Wysocki 	return (platform_mode && hibernation_ops) ?
1758b759b84SRafael J. Wysocki 		hibernation_ops->pre_snapshot() : 0;
1768b759b84SRafael J. Wysocki }
1778b759b84SRafael J. Wysocki 
1788b759b84SRafael J. Wysocki /**
179f42a9813SRafael J. Wysocki  * platform_leave - Call platform to prepare a transition to the working state.
180f42a9813SRafael J. Wysocki  * @platform_mode: Whether or not to use the platform driver.
181f42a9813SRafael J. Wysocki  *
182f42a9813SRafael J. Wysocki  * Use the platform driver prepare to prepare the machine for switching to the
183f42a9813SRafael J. Wysocki  * normal mode of operation.
184f42a9813SRafael J. Wysocki  *
185f42a9813SRafael J. Wysocki  * This routine is called on one CPU with interrupts disabled.
1868b759b84SRafael J. Wysocki  */
platform_leave(int platform_mode)1878b759b84SRafael J. Wysocki static void platform_leave(int platform_mode)
1888b759b84SRafael J. Wysocki {
1898b759b84SRafael J. Wysocki 	if (platform_mode && hibernation_ops)
1908b759b84SRafael J. Wysocki 		hibernation_ops->leave();
1918b759b84SRafael J. Wysocki }
1928b759b84SRafael J. Wysocki 
1938b759b84SRafael J. Wysocki /**
194f42a9813SRafael J. Wysocki  * platform_finish - Call platform to switch the system to the working state.
195f42a9813SRafael J. Wysocki  * @platform_mode: Whether or not to use the platform driver.
196f42a9813SRafael J. Wysocki  *
197f42a9813SRafael J. Wysocki  * Use the platform driver to switch the machine to the normal mode of
198f42a9813SRafael J. Wysocki  * operation.
199f42a9813SRafael J. Wysocki  *
200f42a9813SRafael J. Wysocki  * This routine must be called after platform_prepare().
2018b759b84SRafael J. Wysocki  */
platform_finish(int platform_mode)2028b759b84SRafael J. Wysocki static void platform_finish(int platform_mode)
2038b759b84SRafael J. Wysocki {
2048b759b84SRafael J. Wysocki 	if (platform_mode && hibernation_ops)
2058b759b84SRafael J. Wysocki 		hibernation_ops->finish();
2068b759b84SRafael J. Wysocki }
2078b759b84SRafael J. Wysocki 
2088b759b84SRafael J. Wysocki /**
209f42a9813SRafael J. Wysocki  * platform_pre_restore - Prepare for hibernate image restoration.
210f42a9813SRafael J. Wysocki  * @platform_mode: Whether or not to use the platform driver.
211f42a9813SRafael J. Wysocki  *
212f42a9813SRafael J. Wysocki  * Use the platform driver to prepare the system for resume from a hibernation
213f42a9813SRafael J. Wysocki  * image.
214f42a9813SRafael J. Wysocki  *
215f42a9813SRafael J. Wysocki  * If the restore fails after this function has been called,
216f42a9813SRafael J. Wysocki  * platform_restore_cleanup() must be called.
2178b759b84SRafael J. Wysocki  */
platform_pre_restore(int platform_mode)2188b759b84SRafael J. Wysocki static int platform_pre_restore(int platform_mode)
2198b759b84SRafael J. Wysocki {
2208b759b84SRafael J. Wysocki 	return (platform_mode && hibernation_ops) ?
2218b759b84SRafael J. Wysocki 		hibernation_ops->pre_restore() : 0;
2228b759b84SRafael J. Wysocki }
2238b759b84SRafael J. Wysocki 
2248b759b84SRafael J. Wysocki /**
225f42a9813SRafael J. Wysocki  * platform_restore_cleanup - Switch to the working state after failing restore.
226f42a9813SRafael J. Wysocki  * @platform_mode: Whether or not to use the platform driver.
227f42a9813SRafael J. Wysocki  *
228f42a9813SRafael J. Wysocki  * Use the platform driver to switch the system to the normal mode of operation
229f42a9813SRafael J. Wysocki  * after a failing restore.
230f42a9813SRafael J. Wysocki  *
231f42a9813SRafael J. Wysocki  * If platform_pre_restore() has been called before the failing restore, this
232f42a9813SRafael J. Wysocki  * function must be called too, regardless of the result of
233f42a9813SRafael J. Wysocki  * platform_pre_restore().
2348b759b84SRafael J. Wysocki  */
platform_restore_cleanup(int platform_mode)2358b759b84SRafael J. Wysocki static void platform_restore_cleanup(int platform_mode)
2368b759b84SRafael J. Wysocki {
2378b759b84SRafael J. Wysocki 	if (platform_mode && hibernation_ops)
2388b759b84SRafael J. Wysocki 		hibernation_ops->restore_cleanup();
2398b759b84SRafael J. Wysocki }
2408b759b84SRafael J. Wysocki 
2418b759b84SRafael J. Wysocki /**
242f42a9813SRafael J. Wysocki  * platform_recover - Recover from a failure to suspend devices.
243f42a9813SRafael J. Wysocki  * @platform_mode: Whether or not to use the platform driver.
2448b759b84SRafael J. Wysocki  */
platform_recover(int platform_mode)2458b759b84SRafael J. Wysocki static void platform_recover(int platform_mode)
2468b759b84SRafael J. Wysocki {
2478b759b84SRafael J. Wysocki 	if (platform_mode && hibernation_ops && hibernation_ops->recover)
2488b759b84SRafael J. Wysocki 		hibernation_ops->recover();
2498b759b84SRafael J. Wysocki }
2508b759b84SRafael J. Wysocki 
2518b759b84SRafael J. Wysocki /**
252f42a9813SRafael J. Wysocki  * swsusp_show_speed - Print time elapsed between two events during hibernation.
2538e60c6a1SNigel Cunningham  * @start: Starting event.
2548e60c6a1SNigel Cunningham  * @stop: Final event.
255f42a9813SRafael J. Wysocki  * @nr_pages: Number of memory pages processed between @start and @stop.
256f42a9813SRafael J. Wysocki  * @msg: Additional diagnostic message to print.
2578e60c6a1SNigel Cunningham  */
swsusp_show_speed(ktime_t start,ktime_t stop,unsigned nr_pages,char * msg)258db597605STina Ruchandani void swsusp_show_speed(ktime_t start, ktime_t stop,
2598e60c6a1SNigel Cunningham 		      unsigned nr_pages, char *msg)
2608e60c6a1SNigel Cunningham {
261db597605STina Ruchandani 	ktime_t diff;
2624881f603SChen Gang 	u64 elapsed_centisecs64;
2634881f603SChen Gang 	unsigned int centisecs;
2644881f603SChen Gang 	unsigned int k;
2654881f603SChen Gang 	unsigned int kps;
2668e60c6a1SNigel Cunningham 
267db597605STina Ruchandani 	diff = ktime_sub(stop, start);
268db597605STina Ruchandani 	elapsed_centisecs64 = ktime_divns(diff, 10*NSEC_PER_MSEC);
2698e60c6a1SNigel Cunningham 	centisecs = elapsed_centisecs64;
2708e60c6a1SNigel Cunningham 	if (centisecs == 0)
2718e60c6a1SNigel Cunningham 		centisecs = 1;	/* avoid div-by-zero */
2728e60c6a1SNigel Cunningham 	k = nr_pages * (PAGE_SIZE / 1024);
2738e60c6a1SNigel Cunningham 	kps = (k * 100) / centisecs;
2742872de13SRafael J. Wysocki 	pr_info("%s %u kbytes in %u.%02u seconds (%u.%02u MB/s)\n",
2752872de13SRafael J. Wysocki 		msg, k, centisecs / 100, centisecs % 100, kps / 1000,
2762872de13SRafael J. Wysocki 		(kps % 1000) / 10);
2778e60c6a1SNigel Cunningham }
2788e60c6a1SNigel Cunningham 
arch_resume_nosmt(void)279ec527c31SJiri Kosina __weak int arch_resume_nosmt(void)
280ec527c31SJiri Kosina {
281ec527c31SJiri Kosina 	return 0;
282ec527c31SJiri Kosina }
283ec527c31SJiri Kosina 
2848e60c6a1SNigel Cunningham /**
285f42a9813SRafael J. Wysocki  * create_image - Create a hibernation image.
286f42a9813SRafael J. Wysocki  * @platform_mode: Whether or not to use the platform driver.
287f42a9813SRafael J. Wysocki  *
288cf579dfbSRafael J. Wysocki  * Execute device drivers' "late" and "noirq" freeze callbacks, create a
289cf579dfbSRafael J. Wysocki  * hibernation image and run the drivers' "noirq" and "early" thaw callbacks.
290f42a9813SRafael J. Wysocki  *
291f42a9813SRafael J. Wysocki  * Control reappears in this routine after the subsequent restore.
2928b759b84SRafael J. Wysocki  */
create_image(int platform_mode)2938b759b84SRafael J. Wysocki static int create_image(int platform_mode)
2948b759b84SRafael J. Wysocki {
2958b759b84SRafael J. Wysocki 	int error;
2968b759b84SRafael J. Wysocki 
297cf579dfbSRafael J. Wysocki 	error = dpm_suspend_end(PMSG_FREEZE);
2988b759b84SRafael J. Wysocki 	if (error) {
2997a7b99bfSLuigi Semenzato 		pr_err("Some devices failed to power down, aborting\n");
3008b759b84SRafael J. Wysocki 		return error;
3018b759b84SRafael J. Wysocki 	}
3028b759b84SRafael J. Wysocki 
3038b759b84SRafael J. Wysocki 	error = platform_pre_snapshot(platform_mode);
3048b759b84SRafael J. Wysocki 	if (error || hibernation_test(TEST_PLATFORM))
3058b759b84SRafael J. Wysocki 		goto Platform_finish;
3068b759b84SRafael J. Wysocki 
30723f62d7aSRafael J. Wysocki 	error = pm_sleep_disable_secondary_cpus();
30848580ab8SSrivatsa S. Bhat 	if (error || hibernation_test(TEST_CPUS))
3098b759b84SRafael J. Wysocki 		goto Enable_cpus;
3108b759b84SRafael J. Wysocki 
3118b759b84SRafael J. Wysocki 	local_irq_disable();
3128b759b84SRafael J. Wysocki 
313c1a957d1SThomas Gleixner 	system_state = SYSTEM_SUSPEND;
314c1a957d1SThomas Gleixner 
31540dc166cSRafael J. Wysocki 	error = syscore_suspend();
3168b759b84SRafael J. Wysocki 	if (error) {
3177a7b99bfSLuigi Semenzato 		pr_err("Some system devices failed to power down, aborting\n");
3188b759b84SRafael J. Wysocki 		goto Enable_irqs;
3198b759b84SRafael J. Wysocki 	}
3208b759b84SRafael J. Wysocki 
321a2867e08SRafael J. Wysocki 	if (hibernation_test(TEST_CORE) || pm_wakeup_pending())
3228b759b84SRafael J. Wysocki 		goto Power_up;
3238b759b84SRafael J. Wysocki 
3248b759b84SRafael J. Wysocki 	in_suspend = 1;
3258b759b84SRafael J. Wysocki 	save_processor_state();
326bb3632c6STodd E Brandt 	trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, true);
3278b759b84SRafael J. Wysocki 	error = swsusp_arch_suspend();
32862822e2eSThomas Garnier 	/* Restore control flow magically appears here */
32962822e2eSThomas Garnier 	restore_processor_state();
330bb3632c6STodd E Brandt 	trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, false);
3318b759b84SRafael J. Wysocki 	if (error)
3327a7b99bfSLuigi Semenzato 		pr_err("Error %d creating image\n", error);
3332872de13SRafael J. Wysocki 
3341ad1410fSAnisse Astier 	if (!in_suspend) {
335c125e96fSRafael J. Wysocki 		events_check_enabled = false;
33603b6c9a3SVlastimil Babka 		clear_or_poison_free_pages();
3371ad1410fSAnisse Astier 	}
338362e77d1SBjørn Mork 
3398b759b84SRafael J. Wysocki 	platform_leave(platform_mode);
3408b759b84SRafael J. Wysocki 
3418b759b84SRafael J. Wysocki  Power_up:
34240dc166cSRafael J. Wysocki 	syscore_resume();
3438b759b84SRafael J. Wysocki 
3448b759b84SRafael J. Wysocki  Enable_irqs:
345c1a957d1SThomas Gleixner 	system_state = SYSTEM_RUNNING;
3468b759b84SRafael J. Wysocki 	local_irq_enable();
3478b759b84SRafael J. Wysocki 
3488b759b84SRafael J. Wysocki  Enable_cpus:
34923f62d7aSRafael J. Wysocki 	pm_sleep_enable_secondary_cpus();
3508b759b84SRafael J. Wysocki 
351ec527c31SJiri Kosina 	/* Allow architectures to do nosmt-specific post-resume dances */
352ec527c31SJiri Kosina 	if (!in_suspend)
353ec527c31SJiri Kosina 		error = arch_resume_nosmt();
354ec527c31SJiri Kosina 
3558b759b84SRafael J. Wysocki  Platform_finish:
3568b759b84SRafael J. Wysocki 	platform_finish(platform_mode);
3578b759b84SRafael J. Wysocki 
358cf579dfbSRafael J. Wysocki 	dpm_resume_start(in_suspend ?
3598b759b84SRafael J. Wysocki 		(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
3608b759b84SRafael J. Wysocki 
3618b759b84SRafael J. Wysocki 	return error;
3628b759b84SRafael J. Wysocki }
3638b759b84SRafael J. Wysocki 
3648b759b84SRafael J. Wysocki /**
365f42a9813SRafael J. Wysocki  * hibernation_snapshot - Quiesce devices and create a hibernation image.
366f42a9813SRafael J. Wysocki  * @platform_mode: If set, use platform driver to prepare for the transition.
3678b759b84SRafael J. Wysocki  *
36855f2503cSPingfan Liu  * This routine must be called with system_transition_mutex held.
3698b759b84SRafael J. Wysocki  */
hibernation_snapshot(int platform_mode)3708b759b84SRafael J. Wysocki int hibernation_snapshot(int platform_mode)
3718b759b84SRafael J. Wysocki {
372953a2063SSrivatsa S. Bhat 	pm_message_t msg;
3738b759b84SRafael J. Wysocki 	int error;
3748b759b84SRafael J. Wysocki 
37527614273SLukas Wunner 	pm_suspend_clear_flags();
3768b759b84SRafael J. Wysocki 	error = platform_begin(platform_mode);
3778b759b84SRafael J. Wysocki 	if (error)
378d074ee02SRafael J. Wysocki 		goto Close;
3798b759b84SRafael J. Wysocki 
38064a473cbSRafael J. Wysocki 	/* Preallocate image memory before shutting down devices. */
38164a473cbSRafael J. Wysocki 	error = hibernate_preallocate_memory();
3828b759b84SRafael J. Wysocki 	if (error)
3832aede851SRafael J. Wysocki 		goto Close;
3842aede851SRafael J. Wysocki 
3852aede851SRafael J. Wysocki 	error = freeze_kernel_threads();
3862aede851SRafael J. Wysocki 	if (error)
387bb58dd5dSRafael J. Wysocki 		goto Cleanup;
3882aede851SRafael J. Wysocki 
38948580ab8SSrivatsa S. Bhat 	if (hibernation_test(TEST_FREEZER)) {
390aa9a7b11SSrivatsa S. Bhat 
391aa9a7b11SSrivatsa S. Bhat 		/*
392aa9a7b11SSrivatsa S. Bhat 		 * Indicate to the caller that we are returning due to a
393aa9a7b11SSrivatsa S. Bhat 		 * successful freezer test.
394aa9a7b11SSrivatsa S. Bhat 		 */
395aa9a7b11SSrivatsa S. Bhat 		freezer_test_done = true;
39651d6ff7aSSrivatsa S. Bhat 		goto Thaw;
397aa9a7b11SSrivatsa S. Bhat 	}
398aa9a7b11SSrivatsa S. Bhat 
3992aede851SRafael J. Wysocki 	error = dpm_prepare(PMSG_FREEZE);
400bb58dd5dSRafael J. Wysocki 	if (error) {
401953a2063SSrivatsa S. Bhat 		dpm_complete(PMSG_RECOVER);
40251d6ff7aSSrivatsa S. Bhat 		goto Thaw;
403bb58dd5dSRafael J. Wysocki 	}
4048b759b84SRafael J. Wysocki 
4058b759b84SRafael J. Wysocki 	suspend_console();
406c9e664f1SRafael J. Wysocki 	pm_restrict_gfp_mask();
407953a2063SSrivatsa S. Bhat 
40891e7c75bSRafael J. Wysocki 	error = dpm_suspend(PMSG_FREEZE);
4098b759b84SRafael J. Wysocki 
410953a2063SSrivatsa S. Bhat 	if (error || hibernation_test(TEST_DEVICES))
411953a2063SSrivatsa S. Bhat 		platform_recover(platform_mode);
412953a2063SSrivatsa S. Bhat 	else
4138b759b84SRafael J. Wysocki 		error = create_image(platform_mode);
414953a2063SSrivatsa S. Bhat 
415c9e664f1SRafael J. Wysocki 	/*
416953a2063SSrivatsa S. Bhat 	 * In the case that we call create_image() above, the control
417953a2063SSrivatsa S. Bhat 	 * returns here (1) after the image has been created or the
418c9e664f1SRafael J. Wysocki 	 * image creation has failed and (2) after a successful restore.
419c9e664f1SRafael J. Wysocki 	 */
4208b759b84SRafael J. Wysocki 
42164a473cbSRafael J. Wysocki 	/* We may need to release the preallocated image pages here. */
42264a473cbSRafael J. Wysocki 	if (error || !in_suspend)
42364a473cbSRafael J. Wysocki 		swsusp_free();
42464a473cbSRafael J. Wysocki 
42591e7c75bSRafael J. Wysocki 	msg = in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE;
42691e7c75bSRafael J. Wysocki 	dpm_resume(msg);
427c9e664f1SRafael J. Wysocki 
428c9e664f1SRafael J. Wysocki 	if (error || !in_suspend)
429c9e664f1SRafael J. Wysocki 		pm_restore_gfp_mask();
430c9e664f1SRafael J. Wysocki 
4318b759b84SRafael J. Wysocki 	resume_console();
43291e7c75bSRafael J. Wysocki 	dpm_complete(msg);
43391e7c75bSRafael J. Wysocki 
4348b759b84SRafael J. Wysocki  Close:
4358b759b84SRafael J. Wysocki 	platform_end(platform_mode);
4368b759b84SRafael J. Wysocki 	return error;
4378b759b84SRafael J. Wysocki 
43851d6ff7aSSrivatsa S. Bhat  Thaw:
43951d6ff7aSSrivatsa S. Bhat 	thaw_kernel_threads();
440bb58dd5dSRafael J. Wysocki  Cleanup:
441bb58dd5dSRafael J. Wysocki 	swsusp_free();
442bb58dd5dSRafael J. Wysocki 	goto Close;
4438b759b84SRafael J. Wysocki }
4448b759b84SRafael J. Wysocki 
hibernate_resume_nonboot_cpu_disable(void)445406f992eSRafael J. Wysocki int __weak hibernate_resume_nonboot_cpu_disable(void)
446406f992eSRafael J. Wysocki {
4472f1a6fbbSNicholas Piggin 	return suspend_disable_secondary_cpus();
448406f992eSRafael J. Wysocki }
449406f992eSRafael J. Wysocki 
4508b759b84SRafael J. Wysocki /**
451f42a9813SRafael J. Wysocki  * resume_target_kernel - Restore system state from a hibernation image.
452f42a9813SRafael J. Wysocki  * @platform_mode: Whether or not to use the platform driver.
453f42a9813SRafael J. Wysocki  *
454cf579dfbSRafael J. Wysocki  * Execute device drivers' "noirq" and "late" freeze callbacks, restore the
455cf579dfbSRafael J. Wysocki  * contents of highmem that have not been restored yet from the image and run
456cf579dfbSRafael J. Wysocki  * the low-level code that will restore the remaining contents of memory and
457cf579dfbSRafael J. Wysocki  * switch to the just restored target kernel.
4588b759b84SRafael J. Wysocki  */
resume_target_kernel(bool platform_mode)4598b759b84SRafael J. Wysocki static int resume_target_kernel(bool platform_mode)
4608b759b84SRafael J. Wysocki {
4618b759b84SRafael J. Wysocki 	int error;
4628b759b84SRafael J. Wysocki 
463cf579dfbSRafael J. Wysocki 	error = dpm_suspend_end(PMSG_QUIESCE);
4648b759b84SRafael J. Wysocki 	if (error) {
4652872de13SRafael J. Wysocki 		pr_err("Some devices failed to power down, aborting resume\n");
4668b759b84SRafael J. Wysocki 		return error;
4678b759b84SRafael J. Wysocki 	}
4688b759b84SRafael J. Wysocki 
4698b759b84SRafael J. Wysocki 	error = platform_pre_restore(platform_mode);
4708b759b84SRafael J. Wysocki 	if (error)
4718b759b84SRafael J. Wysocki 		goto Cleanup;
4728b759b84SRafael J. Wysocki 
47323f62d7aSRafael J. Wysocki 	cpuidle_pause();
47423f62d7aSRafael J. Wysocki 
475406f992eSRafael J. Wysocki 	error = hibernate_resume_nonboot_cpu_disable();
4768b759b84SRafael J. Wysocki 	if (error)
4778b759b84SRafael J. Wysocki 		goto Enable_cpus;
4788b759b84SRafael J. Wysocki 
4798b759b84SRafael J. Wysocki 	local_irq_disable();
480c1a957d1SThomas Gleixner 	system_state = SYSTEM_SUSPEND;
4818b759b84SRafael J. Wysocki 
48240dc166cSRafael J. Wysocki 	error = syscore_suspend();
4838b759b84SRafael J. Wysocki 	if (error)
4848b759b84SRafael J. Wysocki 		goto Enable_irqs;
4858b759b84SRafael J. Wysocki 
4868b759b84SRafael J. Wysocki 	save_processor_state();
4878b759b84SRafael J. Wysocki 	error = restore_highmem();
4888b759b84SRafael J. Wysocki 	if (!error) {
4898b759b84SRafael J. Wysocki 		error = swsusp_arch_resume();
4908b759b84SRafael J. Wysocki 		/*
4918b759b84SRafael J. Wysocki 		 * The code below is only ever reached in case of a failure.
4924e2d9491SRafael J. Wysocki 		 * Otherwise, execution continues at the place where
4934e2d9491SRafael J. Wysocki 		 * swsusp_arch_suspend() was called.
4948b759b84SRafael J. Wysocki 		 */
4958b759b84SRafael J. Wysocki 		BUG_ON(!error);
4964e2d9491SRafael J. Wysocki 		/*
4974e2d9491SRafael J. Wysocki 		 * This call to restore_highmem() reverts the changes made by
4984e2d9491SRafael J. Wysocki 		 * the previous one.
4994e2d9491SRafael J. Wysocki 		 */
5008b759b84SRafael J. Wysocki 		restore_highmem();
5018b759b84SRafael J. Wysocki 	}
5028b759b84SRafael J. Wysocki 	/*
5038b759b84SRafael J. Wysocki 	 * The only reason why swsusp_arch_resume() can fail is memory being
5048b759b84SRafael J. Wysocki 	 * very tight, so we have to free it as soon as we can to avoid
5054e2d9491SRafael J. Wysocki 	 * subsequent failures.
5068b759b84SRafael J. Wysocki 	 */
5078b759b84SRafael J. Wysocki 	swsusp_free();
5088b759b84SRafael J. Wysocki 	restore_processor_state();
5098b759b84SRafael J. Wysocki 	touch_softlockup_watchdog();
5108b759b84SRafael J. Wysocki 
51140dc166cSRafael J. Wysocki 	syscore_resume();
5128b759b84SRafael J. Wysocki 
5138b759b84SRafael J. Wysocki  Enable_irqs:
514c1a957d1SThomas Gleixner 	system_state = SYSTEM_RUNNING;
5158b759b84SRafael J. Wysocki 	local_irq_enable();
5168b759b84SRafael J. Wysocki 
5178b759b84SRafael J. Wysocki  Enable_cpus:
51823f62d7aSRafael J. Wysocki 	pm_sleep_enable_secondary_cpus();
5198b759b84SRafael J. Wysocki 
5208b759b84SRafael J. Wysocki  Cleanup:
5218b759b84SRafael J. Wysocki 	platform_restore_cleanup(platform_mode);
5228b759b84SRafael J. Wysocki 
523cf579dfbSRafael J. Wysocki 	dpm_resume_start(PMSG_RECOVER);
5248b759b84SRafael J. Wysocki 
5258b759b84SRafael J. Wysocki 	return error;
5268b759b84SRafael J. Wysocki }
5278b759b84SRafael J. Wysocki 
5288b759b84SRafael J. Wysocki /**
529f42a9813SRafael J. Wysocki  * hibernation_restore - Quiesce devices and restore from a hibernation image.
530f42a9813SRafael J. Wysocki  * @platform_mode: If set, use platform driver to prepare for the transition.
5318b759b84SRafael J. Wysocki  *
53255f2503cSPingfan Liu  * This routine must be called with system_transition_mutex held.  If it is
53355f2503cSPingfan Liu  * successful, control reappears in the restored target kernel in
53455f2503cSPingfan Liu  * hibernation_snapshot().
5358b759b84SRafael J. Wysocki  */
hibernation_restore(int platform_mode)5368b759b84SRafael J. Wysocki int hibernation_restore(int platform_mode)
5378b759b84SRafael J. Wysocki {
5388b759b84SRafael J. Wysocki 	int error;
5398b759b84SRafael J. Wysocki 
5408b759b84SRafael J. Wysocki 	pm_prepare_console();
5418b759b84SRafael J. Wysocki 	suspend_console();
542c9e664f1SRafael J. Wysocki 	pm_restrict_gfp_mask();
5438b759b84SRafael J. Wysocki 	error = dpm_suspend_start(PMSG_QUIESCE);
5448b759b84SRafael J. Wysocki 	if (!error) {
5458b759b84SRafael J. Wysocki 		error = resume_target_kernel(platform_mode);
54694fb823fSImre Deak 		/*
54794fb823fSImre Deak 		 * The above should either succeed and jump to the new kernel,
54894fb823fSImre Deak 		 * or return with an error. Otherwise things are just
54994fb823fSImre Deak 		 * undefined, so let's be paranoid.
55094fb823fSImre Deak 		 */
55194fb823fSImre Deak 		BUG_ON(!error);
5528b759b84SRafael J. Wysocki 	}
55394fb823fSImre Deak 	dpm_resume_end(PMSG_RECOVER);
554c9e664f1SRafael J. Wysocki 	pm_restore_gfp_mask();
5558b759b84SRafael J. Wysocki 	resume_console();
5568b759b84SRafael J. Wysocki 	pm_restore_console();
5578b759b84SRafael J. Wysocki 	return error;
5588b759b84SRafael J. Wysocki }
5598b759b84SRafael J. Wysocki 
5608b759b84SRafael J. Wysocki /**
561f42a9813SRafael J. Wysocki  * hibernation_platform_enter - Power off the system using the platform driver.
5628b759b84SRafael J. Wysocki  */
hibernation_platform_enter(void)5638b759b84SRafael J. Wysocki int hibernation_platform_enter(void)
5648b759b84SRafael J. Wysocki {
5658b759b84SRafael J. Wysocki 	int error;
5668b759b84SRafael J. Wysocki 
5678b759b84SRafael J. Wysocki 	if (!hibernation_ops)
5688b759b84SRafael J. Wysocki 		return -ENOSYS;
5698b759b84SRafael J. Wysocki 
5708b759b84SRafael J. Wysocki 	/*
5718b759b84SRafael J. Wysocki 	 * We have cancelled the power transition by running
5728b759b84SRafael J. Wysocki 	 * hibernation_ops->finish() before saving the image, so we should let
5738b759b84SRafael J. Wysocki 	 * the firmware know that we're going to enter the sleep state after all
5748b759b84SRafael J. Wysocki 	 */
575bb186901SRafael J. Wysocki 	error = hibernation_ops->begin(PMSG_HIBERNATE);
5768b759b84SRafael J. Wysocki 	if (error)
5778b759b84SRafael J. Wysocki 		goto Close;
5788b759b84SRafael J. Wysocki 
5798b759b84SRafael J. Wysocki 	entering_platform_hibernation = true;
5808b759b84SRafael J. Wysocki 	suspend_console();
5818b759b84SRafael J. Wysocki 	error = dpm_suspend_start(PMSG_HIBERNATE);
5828b759b84SRafael J. Wysocki 	if (error) {
5838b759b84SRafael J. Wysocki 		if (hibernation_ops->recover)
5848b759b84SRafael J. Wysocki 			hibernation_ops->recover();
5858b759b84SRafael J. Wysocki 		goto Resume_devices;
5868b759b84SRafael J. Wysocki 	}
5878b759b84SRafael J. Wysocki 
588cf579dfbSRafael J. Wysocki 	error = dpm_suspend_end(PMSG_HIBERNATE);
5898b759b84SRafael J. Wysocki 	if (error)
5908b759b84SRafael J. Wysocki 		goto Resume_devices;
5918b759b84SRafael J. Wysocki 
5928b759b84SRafael J. Wysocki 	error = hibernation_ops->prepare();
5938b759b84SRafael J. Wysocki 	if (error)
594e681c9ddSThadeu Lima de Souza Cascardo 		goto Platform_finish;
5958b759b84SRafael J. Wysocki 
59623f62d7aSRafael J. Wysocki 	error = pm_sleep_disable_secondary_cpus();
5978b759b84SRafael J. Wysocki 	if (error)
5988c506608SVitaly Kuznetsov 		goto Enable_cpus;
5998b759b84SRafael J. Wysocki 
6008b759b84SRafael J. Wysocki 	local_irq_disable();
601c1a957d1SThomas Gleixner 	system_state = SYSTEM_SUSPEND;
602*02794e35SWentao Liang 
603*02794e35SWentao Liang 	error = syscore_suspend();
604*02794e35SWentao Liang 	if (error)
605*02794e35SWentao Liang 		goto Enable_irqs;
606*02794e35SWentao Liang 
607a2867e08SRafael J. Wysocki 	if (pm_wakeup_pending()) {
608c125e96fSRafael J. Wysocki 		error = -EAGAIN;
609c125e96fSRafael J. Wysocki 		goto Power_up;
610c125e96fSRafael J. Wysocki 	}
611c125e96fSRafael J. Wysocki 
6128b759b84SRafael J. Wysocki 	hibernation_ops->enter();
6138b759b84SRafael J. Wysocki 	/* We should never get here */
6148b759b84SRafael J. Wysocki 	while (1);
6158b759b84SRafael J. Wysocki 
616c125e96fSRafael J. Wysocki  Power_up:
61740dc166cSRafael J. Wysocki 	syscore_resume();
618*02794e35SWentao Liang  Enable_irqs:
619c1a957d1SThomas Gleixner 	system_state = SYSTEM_RUNNING;
620c125e96fSRafael J. Wysocki 	local_irq_enable();
6218c506608SVitaly Kuznetsov 
6228c506608SVitaly Kuznetsov  Enable_cpus:
62323f62d7aSRafael J. Wysocki 	pm_sleep_enable_secondary_cpus();
624c125e96fSRafael J. Wysocki 
625e681c9ddSThadeu Lima de Souza Cascardo  Platform_finish:
6268b759b84SRafael J. Wysocki 	hibernation_ops->finish();
6278b759b84SRafael J. Wysocki 
628cf579dfbSRafael J. Wysocki 	dpm_resume_start(PMSG_RESTORE);
6298b759b84SRafael J. Wysocki 
6308b759b84SRafael J. Wysocki  Resume_devices:
6318b759b84SRafael J. Wysocki 	entering_platform_hibernation = false;
6328b759b84SRafael J. Wysocki 	dpm_resume_end(PMSG_RESTORE);
6338b759b84SRafael J. Wysocki 	resume_console();
6348b759b84SRafael J. Wysocki 
6358b759b84SRafael J. Wysocki  Close:
6368b759b84SRafael J. Wysocki 	hibernation_ops->end();
6378b759b84SRafael J. Wysocki 
6388b759b84SRafael J. Wysocki 	return error;
6398b759b84SRafael J. Wysocki }
6408b759b84SRafael J. Wysocki 
6418b759b84SRafael J. Wysocki /**
6428b759b84SRafael J. Wysocki  * power_down - Shut the machine down for hibernation.
6438b759b84SRafael J. Wysocki  *
644f42a9813SRafael J. Wysocki  * Use the platform driver, if configured, to put the system into the sleep
645f42a9813SRafael J. Wysocki  * state corresponding to hibernation, or try to power it off or reboot,
646f42a9813SRafael J. Wysocki  * depending on the value of hibernation_mode.
6478b759b84SRafael J. Wysocki  */
power_down(void)6488b759b84SRafael J. Wysocki static void power_down(void)
6498b759b84SRafael J. Wysocki {
65062c552ccSBojan Smojver #ifdef CONFIG_SUSPEND
65162c552ccSBojan Smojver 	int error;
65281d45bdfSRafael J. Wysocki 
65381d45bdfSRafael J. Wysocki 	if (hibernation_mode == HIBERNATION_SUSPEND) {
65485850af4SMario Limonciello 		error = suspend_devices_and_enter(mem_sleep_current);
65581d45bdfSRafael J. Wysocki 		if (error) {
65681d45bdfSRafael J. Wysocki 			hibernation_mode = hibernation_ops ?
65781d45bdfSRafael J. Wysocki 						HIBERNATION_PLATFORM :
65881d45bdfSRafael J. Wysocki 						HIBERNATION_SHUTDOWN;
65981d45bdfSRafael J. Wysocki 		} else {
66081d45bdfSRafael J. Wysocki 			/* Restore swap signature. */
66181d45bdfSRafael J. Wysocki 			error = swsusp_unmark();
66281d45bdfSRafael J. Wysocki 			if (error)
6632872de13SRafael J. Wysocki 				pr_err("Swap will be unusable! Try swapon -a.\n");
66481d45bdfSRafael J. Wysocki 
66581d45bdfSRafael J. Wysocki 			return;
66681d45bdfSRafael J. Wysocki 		}
66781d45bdfSRafael J. Wysocki 	}
66862c552ccSBojan Smojver #endif
66962c552ccSBojan Smojver 
6708b759b84SRafael J. Wysocki 	switch (hibernation_mode) {
6718b759b84SRafael J. Wysocki 	case HIBERNATION_REBOOT:
6728b759b84SRafael J. Wysocki 		kernel_restart(NULL);
6738b759b84SRafael J. Wysocki 		break;
6748b759b84SRafael J. Wysocki 	case HIBERNATION_PLATFORM:
6758b759b84SRafael J. Wysocki 		hibernation_platform_enter();
676df561f66SGustavo A. R. Silva 		fallthrough;
6778b759b84SRafael J. Wysocki 	case HIBERNATION_SHUTDOWN:
67820277326SDmitry Osipenko 		if (kernel_can_power_off())
6798b759b84SRafael J. Wysocki 			kernel_power_off();
6808b759b84SRafael J. Wysocki 		break;
6818b759b84SRafael J. Wysocki 	}
6828b759b84SRafael J. Wysocki 	kernel_halt();
6838b759b84SRafael J. Wysocki 	/*
6848b759b84SRafael J. Wysocki 	 * Valid image is on the disk, if we continue we risk serious data
6858b759b84SRafael J. Wysocki 	 * corruption after resume.
6868b759b84SRafael J. Wysocki 	 */
6872872de13SRafael J. Wysocki 	pr_crit("Power down manually\n");
6882c730785SSebastian Capella 	while (1)
6892c730785SSebastian Capella 		cpu_relax();
6908b759b84SRafael J. Wysocki }
6918b759b84SRafael J. Wysocki 
load_image_and_restore(bool snapshot_test)692d6545e68SChristoph Hellwig static int load_image_and_restore(bool snapshot_test)
693fe12c00dSChen Yu {
694fe12c00dSChen Yu 	int error;
695fe12c00dSChen Yu 	unsigned int flags;
696fe12c00dSChen Yu 
6978d8b2441SRafael J. Wysocki 	pm_pr_dbg("Loading hibernation image.\n");
698fe12c00dSChen Yu 
699fe12c00dSChen Yu 	lock_device_hotplug();
700fe12c00dSChen Yu 	error = create_basic_memory_bitmaps();
7013f51aa9eSYe Bin 	if (error) {
7022736e8eeSChristoph Hellwig 		swsusp_close(snapshot_test);
703fe12c00dSChen Yu 		goto Unlock;
7043f51aa9eSYe Bin 	}
705fe12c00dSChen Yu 
706fe12c00dSChen Yu 	error = swsusp_read(&flags);
7072736e8eeSChristoph Hellwig 	swsusp_close(snapshot_test);
708fe12c00dSChen Yu 	if (!error)
7093704a6a4SDexuan Cui 		error = hibernation_restore(flags & SF_PLATFORM_MODE);
710fe12c00dSChen Yu 
7117a7b99bfSLuigi Semenzato 	pr_err("Failed to load image, recovering.\n");
712fe12c00dSChen Yu 	swsusp_free();
713fe12c00dSChen Yu 	free_basic_memory_bitmaps();
714fe12c00dSChen Yu  Unlock:
715fe12c00dSChen Yu 	unlock_device_hotplug();
716fe12c00dSChen Yu 
717fe12c00dSChen Yu 	return error;
718fe12c00dSChen Yu }
719fe12c00dSChen Yu 
7208b759b84SRafael J. Wysocki /**
721f42a9813SRafael J. Wysocki  * hibernate - Carry out system hibernation, including saving the image.
7228b759b84SRafael J. Wysocki  */
hibernate(void)7238b759b84SRafael J. Wysocki int hibernate(void)
7248b759b84SRafael J. Wysocki {
725d6545e68SChristoph Hellwig 	bool snapshot_test = false;
7265950e5d5SPeter Zijlstra 	unsigned int sleep_flags;
72770d93298SPeter Zijlstra 	int error;
7288b759b84SRafael J. Wysocki 
729a6e15a39SKees Cook 	if (!hibernation_available()) {
7308d8b2441SRafael J. Wysocki 		pm_pr_dbg("Hibernation not available.\n");
731a6e15a39SKees Cook 		return -EPERM;
732a6e15a39SKees Cook 	}
733a6e15a39SKees Cook 
7345950e5d5SPeter Zijlstra 	sleep_flags = lock_system_sleep();
7358b759b84SRafael J. Wysocki 	/* The snapshot device should not be opened while we're running */
736ab7e9b06SDomenico Andreoli 	if (!hibernate_acquire()) {
7378b759b84SRafael J. Wysocki 		error = -EBUSY;
7388b759b84SRafael J. Wysocki 		goto Unlock;
7398b759b84SRafael J. Wysocki 	}
7408b759b84SRafael J. Wysocki 
7418915aa20SRafael J. Wysocki 	pr_info("hibernation entry\n");
7428b759b84SRafael J. Wysocki 	pm_prepare_console();
74370d93298SPeter Zijlstra 	error = pm_notifier_call_chain_robust(PM_HIBERNATION_PREPARE, PM_POST_HIBERNATION);
74470d93298SPeter Zijlstra 	if (error)
74570d93298SPeter Zijlstra 		goto Restore;
7468b759b84SRafael J. Wysocki 
747b5dee313SHarry Pan 	ksys_sync_helper();
7488b759b84SRafael J. Wysocki 
74903afed8bSTejun Heo 	error = freeze_processes();
7508b759b84SRafael J. Wysocki 	if (error)
7518fd37a4cSRafael J. Wysocki 		goto Exit;
7528fd37a4cSRafael J. Wysocki 
753942f4015SRafael J. Wysocki 	lock_device_hotplug();
7548fd37a4cSRafael J. Wysocki 	/* Allocate memory management structures */
7558fd37a4cSRafael J. Wysocki 	error = create_basic_memory_bitmaps();
7568fd37a4cSRafael J. Wysocki 	if (error)
7578fd37a4cSRafael J. Wysocki 		goto Thaw;
7588b759b84SRafael J. Wysocki 
7598b759b84SRafael J. Wysocki 	error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);
760a556d5b5SSrivatsa S. Bhat 	if (error || freezer_test_done)
7618fd37a4cSRafael J. Wysocki 		goto Free_bitmaps;
76264a473cbSRafael J. Wysocki 
76364a473cbSRafael J. Wysocki 	if (in_suspend) {
7648b759b84SRafael J. Wysocki 		unsigned int flags = 0;
7658b759b84SRafael J. Wysocki 
7668b759b84SRafael J. Wysocki 		if (hibernation_mode == HIBERNATION_PLATFORM)
7678b759b84SRafael J. Wysocki 			flags |= SF_PLATFORM_MODE;
768f996fc96SBojan Smojver 		if (nocompress)
769f996fc96SBojan Smojver 			flags |= SF_NOCOMPRESS_MODE;
770081a9d04SBojan Smojver 		else
771081a9d04SBojan Smojver 		        flags |= SF_CRC32_MODE;
772081a9d04SBojan Smojver 
7737a7b99bfSLuigi Semenzato 		pm_pr_dbg("Writing hibernation image.\n");
7748b759b84SRafael J. Wysocki 		error = swsusp_write(flags);
7758b759b84SRafael J. Wysocki 		swsusp_free();
776fe12c00dSChen Yu 		if (!error) {
777fe12c00dSChen Yu 			if (hibernation_mode == HIBERNATION_TEST_RESUME)
778fe12c00dSChen Yu 				snapshot_test = true;
779fe12c00dSChen Yu 			else
7808b759b84SRafael J. Wysocki 				power_down();
781fe12c00dSChen Yu 		}
7825262a475SMyungJoo Ham 		in_suspend = 0;
783c9e664f1SRafael J. Wysocki 		pm_restore_gfp_mask();
7848b759b84SRafael J. Wysocki 	} else {
7857a7b99bfSLuigi Semenzato 		pm_pr_dbg("Hibernation image restored successfully.\n");
7868b759b84SRafael J. Wysocki 	}
78764a473cbSRafael J. Wysocki 
7888fd37a4cSRafael J. Wysocki  Free_bitmaps:
7898fd37a4cSRafael J. Wysocki 	free_basic_memory_bitmaps();
7908b759b84SRafael J. Wysocki  Thaw:
791942f4015SRafael J. Wysocki 	unlock_device_hotplug();
792fe12c00dSChen Yu 	if (snapshot_test) {
7938d8b2441SRafael J. Wysocki 		pm_pr_dbg("Checking hibernation image\n");
794148b6f4cSChen Yu 		error = swsusp_check(false);
795fe12c00dSChen Yu 		if (!error)
796148b6f4cSChen Yu 			error = load_image_and_restore(false);
797fe12c00dSChen Yu 	}
7988b759b84SRafael J. Wysocki 	thaw_processes();
799a556d5b5SSrivatsa S. Bhat 
800a556d5b5SSrivatsa S. Bhat 	/* Don't bother checking whether freezer_test_done is true */
801a556d5b5SSrivatsa S. Bhat 	freezer_test_done = false;
8028b759b84SRafael J. Wysocki  Exit:
80370d93298SPeter Zijlstra 	pm_notifier_call_chain(PM_POST_HIBERNATION);
80470d93298SPeter Zijlstra  Restore:
8058b759b84SRafael J. Wysocki 	pm_restore_console();
806ab7e9b06SDomenico Andreoli 	hibernate_release();
8078b759b84SRafael J. Wysocki  Unlock:
8085950e5d5SPeter Zijlstra 	unlock_system_sleep(sleep_flags);
8098915aa20SRafael J. Wysocki 	pr_info("hibernation exit\n");
8108915aa20SRafael J. Wysocki 
8118b759b84SRafael J. Wysocki 	return error;
8128b759b84SRafael J. Wysocki }
8138b759b84SRafael J. Wysocki 
81448001ea5SDan Williams /**
81548001ea5SDan Williams  * hibernate_quiet_exec - Execute a function with all devices frozen.
81648001ea5SDan Williams  * @func: Function to execute.
81748001ea5SDan Williams  * @data: Data pointer to pass to @func.
81848001ea5SDan Williams  *
81948001ea5SDan Williams  * Return the @func return value or an error code if it cannot be executed.
82048001ea5SDan Williams  */
hibernate_quiet_exec(int (* func)(void * data),void * data)82148001ea5SDan Williams int hibernate_quiet_exec(int (*func)(void *data), void *data)
82248001ea5SDan Williams {
8235950e5d5SPeter Zijlstra 	unsigned int sleep_flags;
82470d93298SPeter Zijlstra 	int error;
82548001ea5SDan Williams 
8265950e5d5SPeter Zijlstra 	sleep_flags = lock_system_sleep();
82748001ea5SDan Williams 
82848001ea5SDan Williams 	if (!hibernate_acquire()) {
82948001ea5SDan Williams 		error = -EBUSY;
83048001ea5SDan Williams 		goto unlock;
83148001ea5SDan Williams 	}
83248001ea5SDan Williams 
83348001ea5SDan Williams 	pm_prepare_console();
83448001ea5SDan Williams 
83570d93298SPeter Zijlstra 	error = pm_notifier_call_chain_robust(PM_HIBERNATION_PREPARE, PM_POST_HIBERNATION);
83670d93298SPeter Zijlstra 	if (error)
83770d93298SPeter Zijlstra 		goto restore;
83848001ea5SDan Williams 
83948001ea5SDan Williams 	error = freeze_processes();
84048001ea5SDan Williams 	if (error)
84148001ea5SDan Williams 		goto exit;
84248001ea5SDan Williams 
84348001ea5SDan Williams 	lock_device_hotplug();
84448001ea5SDan Williams 
84548001ea5SDan Williams 	pm_suspend_clear_flags();
84648001ea5SDan Williams 
84748001ea5SDan Williams 	error = platform_begin(true);
84848001ea5SDan Williams 	if (error)
84948001ea5SDan Williams 		goto thaw;
85048001ea5SDan Williams 
85148001ea5SDan Williams 	error = freeze_kernel_threads();
85248001ea5SDan Williams 	if (error)
85348001ea5SDan Williams 		goto thaw;
85448001ea5SDan Williams 
85548001ea5SDan Williams 	error = dpm_prepare(PMSG_FREEZE);
85648001ea5SDan Williams 	if (error)
85748001ea5SDan Williams 		goto dpm_complete;
85848001ea5SDan Williams 
85948001ea5SDan Williams 	suspend_console();
86048001ea5SDan Williams 
86148001ea5SDan Williams 	error = dpm_suspend(PMSG_FREEZE);
86248001ea5SDan Williams 	if (error)
86348001ea5SDan Williams 		goto dpm_resume;
86448001ea5SDan Williams 
86548001ea5SDan Williams 	error = dpm_suspend_end(PMSG_FREEZE);
86648001ea5SDan Williams 	if (error)
86748001ea5SDan Williams 		goto dpm_resume;
86848001ea5SDan Williams 
86948001ea5SDan Williams 	error = platform_pre_snapshot(true);
87048001ea5SDan Williams 	if (error)
87148001ea5SDan Williams 		goto skip;
87248001ea5SDan Williams 
87348001ea5SDan Williams 	error = func(data);
87448001ea5SDan Williams 
87548001ea5SDan Williams skip:
87648001ea5SDan Williams 	platform_finish(true);
87748001ea5SDan Williams 
87848001ea5SDan Williams 	dpm_resume_start(PMSG_THAW);
87948001ea5SDan Williams 
88048001ea5SDan Williams dpm_resume:
88148001ea5SDan Williams 	dpm_resume(PMSG_THAW);
88248001ea5SDan Williams 
88348001ea5SDan Williams 	resume_console();
88448001ea5SDan Williams 
88548001ea5SDan Williams dpm_complete:
88648001ea5SDan Williams 	dpm_complete(PMSG_THAW);
88748001ea5SDan Williams 
88848001ea5SDan Williams 	thaw_kernel_threads();
88948001ea5SDan Williams 
89048001ea5SDan Williams thaw:
89148001ea5SDan Williams 	platform_end(true);
89248001ea5SDan Williams 
89348001ea5SDan Williams 	unlock_device_hotplug();
89448001ea5SDan Williams 
89548001ea5SDan Williams 	thaw_processes();
89648001ea5SDan Williams 
89748001ea5SDan Williams exit:
89870d93298SPeter Zijlstra 	pm_notifier_call_chain(PM_POST_HIBERNATION);
89948001ea5SDan Williams 
90070d93298SPeter Zijlstra restore:
90148001ea5SDan Williams 	pm_restore_console();
90248001ea5SDan Williams 
90348001ea5SDan Williams 	hibernate_release();
90448001ea5SDan Williams 
90548001ea5SDan Williams unlock:
9065950e5d5SPeter Zijlstra 	unlock_system_sleep(sleep_flags);
90748001ea5SDan Williams 
90848001ea5SDan Williams 	return error;
90948001ea5SDan Williams }
91048001ea5SDan Williams EXPORT_SYMBOL_GPL(hibernate_quiet_exec);
9118b759b84SRafael J. Wysocki 
find_resume_device(void)912cc89c63eSChristoph Hellwig static int __init find_resume_device(void)
91302b42d58SChristoph Hellwig {
91402b42d58SChristoph Hellwig 	if (!strlen(resume_file))
91502b42d58SChristoph Hellwig 		return -ENOENT;
91602b42d58SChristoph Hellwig 
91702b42d58SChristoph Hellwig 	pm_pr_dbg("Checking hibernation image partition %s\n", resume_file);
91802b42d58SChristoph Hellwig 
91902b42d58SChristoph Hellwig 	if (resume_delay) {
92002b42d58SChristoph Hellwig 		pr_info("Waiting %dsec before reading resume device ...\n",
92102b42d58SChristoph Hellwig 			resume_delay);
92202b42d58SChristoph Hellwig 		ssleep(resume_delay);
92302b42d58SChristoph Hellwig 	}
92402b42d58SChristoph Hellwig 
92502b42d58SChristoph Hellwig 	/* Check if the device is there */
926cf056a43SChristoph Hellwig 	if (!early_lookup_bdev(resume_file, &swsusp_resume_device))
92702b42d58SChristoph Hellwig 		return 0;
92802b42d58SChristoph Hellwig 
92902b42d58SChristoph Hellwig 	/*
93002b42d58SChristoph Hellwig 	 * Some device discovery might still be in progress; we need to wait for
93102b42d58SChristoph Hellwig 	 * this to finish.
93202b42d58SChristoph Hellwig 	 */
93302b42d58SChristoph Hellwig 	wait_for_device_probe();
93402b42d58SChristoph Hellwig 	if (resume_wait) {
935cf056a43SChristoph Hellwig 		while (early_lookup_bdev(resume_file, &swsusp_resume_device))
93602b42d58SChristoph Hellwig 			msleep(10);
93702b42d58SChristoph Hellwig 		async_synchronize_full();
93802b42d58SChristoph Hellwig 	}
93902b42d58SChristoph Hellwig 
940cf056a43SChristoph Hellwig 	return early_lookup_bdev(resume_file, &swsusp_resume_device);
94102b42d58SChristoph Hellwig }
94202b42d58SChristoph Hellwig 
software_resume(void)9438b759b84SRafael J. Wysocki static int software_resume(void)
9448b759b84SRafael J. Wysocki {
94570d93298SPeter Zijlstra 	int error;
9468b759b84SRafael J. Wysocki 
9478d8b2441SRafael J. Wysocki 	pm_pr_dbg("Hibernation image partition %d:%d present\n",
9488b759b84SRafael J. Wysocki 		MAJOR(swsusp_resume_device), MINOR(swsusp_resume_device));
9498b759b84SRafael J. Wysocki 
9508d8b2441SRafael J. Wysocki 	pm_pr_dbg("Looking for hibernation image.\n");
951cc89c63eSChristoph Hellwig 
952cc89c63eSChristoph Hellwig 	mutex_lock(&system_transition_mutex);
953148b6f4cSChen Yu 	error = swsusp_check(true);
9548b759b84SRafael J. Wysocki 	if (error)
9558b759b84SRafael J. Wysocki 		goto Unlock;
9568b759b84SRafael J. Wysocki 
9578b759b84SRafael J. Wysocki 	/* The snapshot device should not be opened while we're running */
958ab7e9b06SDomenico Andreoli 	if (!hibernate_acquire()) {
9598b759b84SRafael J. Wysocki 		error = -EBUSY;
960148b6f4cSChen Yu 		swsusp_close(true);
9618b759b84SRafael J. Wysocki 		goto Unlock;
9628b759b84SRafael J. Wysocki 	}
9638b759b84SRafael J. Wysocki 
9648915aa20SRafael J. Wysocki 	pr_info("resume from hibernation\n");
9658b759b84SRafael J. Wysocki 	pm_prepare_console();
96670d93298SPeter Zijlstra 	error = pm_notifier_call_chain_robust(PM_RESTORE_PREPARE, PM_POST_RESTORE);
96770d93298SPeter Zijlstra 	if (error)
96870d93298SPeter Zijlstra 		goto Restore;
9698b759b84SRafael J. Wysocki 
9707a7b99bfSLuigi Semenzato 	pm_pr_dbg("Preparing processes for hibernation restore.\n");
97103afed8bSTejun Heo 	error = freeze_processes();
9728fd37a4cSRafael J. Wysocki 	if (error)
9738fd37a4cSRafael J. Wysocki 		goto Close_Finish;
9742351f8d2SDexuan Cui 
9752351f8d2SDexuan Cui 	error = freeze_kernel_threads();
9762351f8d2SDexuan Cui 	if (error) {
9772351f8d2SDexuan Cui 		thaw_processes();
9782351f8d2SDexuan Cui 		goto Close_Finish;
9792351f8d2SDexuan Cui 	}
9802351f8d2SDexuan Cui 
981148b6f4cSChen Yu 	error = load_image_and_restore(true);
9828fd37a4cSRafael J. Wysocki 	thaw_processes();
9838b759b84SRafael J. Wysocki  Finish:
98470d93298SPeter Zijlstra 	pm_notifier_call_chain(PM_POST_RESTORE);
98570d93298SPeter Zijlstra  Restore:
9868b759b84SRafael J. Wysocki 	pm_restore_console();
9877a7b99bfSLuigi Semenzato 	pr_info("resume failed (%d)\n", error);
988ab7e9b06SDomenico Andreoli 	hibernate_release();
9898b759b84SRafael J. Wysocki 	/* For success case, the suspend path will release the lock */
9908b759b84SRafael J. Wysocki  Unlock:
99155f2503cSPingfan Liu 	mutex_unlock(&system_transition_mutex);
9928d8b2441SRafael J. Wysocki 	pm_pr_dbg("Hibernation image not present or could not be loaded.\n");
9938b759b84SRafael J. Wysocki 	return error;
9948fd37a4cSRafael J. Wysocki  Close_Finish:
995148b6f4cSChen Yu 	swsusp_close(true);
99676b57e61SJiri Slaby 	goto Finish;
9978b759b84SRafael J. Wysocki }
9988b759b84SRafael J. Wysocki 
999cc89c63eSChristoph Hellwig /**
1000cc89c63eSChristoph Hellwig  * software_resume_initcall - Resume from a saved hibernation image.
1001cc89c63eSChristoph Hellwig  *
1002cc89c63eSChristoph Hellwig  * This routine is called as a late initcall, when all devices have been
1003cc89c63eSChristoph Hellwig  * discovered and initialized already.
1004cc89c63eSChristoph Hellwig  *
1005cc89c63eSChristoph Hellwig  * The image reading code is called to see if there is a hibernation image
1006cc89c63eSChristoph Hellwig  * available for reading.  If that is the case, devices are quiesced and the
1007cc89c63eSChristoph Hellwig  * contents of memory is restored from the saved image.
1008cc89c63eSChristoph Hellwig  *
1009cc89c63eSChristoph Hellwig  * If this is successful, control reappears in the restored target kernel in
1010cc89c63eSChristoph Hellwig  * hibernation_snapshot() which returns to hibernate().  Otherwise, the routine
1011cc89c63eSChristoph Hellwig  * attempts to recover gracefully and make the kernel return to the normal mode
1012cc89c63eSChristoph Hellwig  * of operation.
1013cc89c63eSChristoph Hellwig  */
software_resume_initcall(void)1014cc89c63eSChristoph Hellwig static int __init software_resume_initcall(void)
1015cc89c63eSChristoph Hellwig {
1016cc89c63eSChristoph Hellwig 	/*
1017cc89c63eSChristoph Hellwig 	 * If the user said "noresume".. bail out early.
1018cc89c63eSChristoph Hellwig 	 */
1019cc89c63eSChristoph Hellwig 	if (noresume || !hibernation_available())
1020cc89c63eSChristoph Hellwig 		return 0;
1021cc89c63eSChristoph Hellwig 
1022cc89c63eSChristoph Hellwig 	if (!swsusp_resume_device) {
1023cc89c63eSChristoph Hellwig 		int error = find_resume_device();
1024cc89c63eSChristoph Hellwig 
1025cc89c63eSChristoph Hellwig 		if (error)
1026cc89c63eSChristoph Hellwig 			return error;
1027cc89c63eSChristoph Hellwig 	}
1028cc89c63eSChristoph Hellwig 
1029cc89c63eSChristoph Hellwig 	return software_resume();
1030cc89c63eSChristoph Hellwig }
1031cc89c63eSChristoph Hellwig late_initcall_sync(software_resume_initcall);
10328b759b84SRafael J. Wysocki 
10338b759b84SRafael J. Wysocki 
10348b759b84SRafael J. Wysocki static const char * const hibernation_modes[] = {
10358b759b84SRafael J. Wysocki 	[HIBERNATION_PLATFORM]	= "platform",
10368b759b84SRafael J. Wysocki 	[HIBERNATION_SHUTDOWN]	= "shutdown",
10378b759b84SRafael J. Wysocki 	[HIBERNATION_REBOOT]	= "reboot",
103862c552ccSBojan Smojver #ifdef CONFIG_SUSPEND
103962c552ccSBojan Smojver 	[HIBERNATION_SUSPEND]	= "suspend",
104062c552ccSBojan Smojver #endif
1041fe12c00dSChen Yu 	[HIBERNATION_TEST_RESUME]	= "test_resume",
10428b759b84SRafael J. Wysocki };
10438b759b84SRafael J. Wysocki 
1044f42a9813SRafael J. Wysocki /*
1045f42a9813SRafael J. Wysocki  * /sys/power/disk - Control hibernation mode.
10468b759b84SRafael J. Wysocki  *
1047f42a9813SRafael J. Wysocki  * Hibernation can be handled in several ways.  There are a few different ways
1048f42a9813SRafael J. Wysocki  * to put the system into the sleep state: using the platform driver (e.g. ACPI
1049f42a9813SRafael J. Wysocki  * or other hibernation_ops), powering it off or rebooting it (for testing
105048580ab8SSrivatsa S. Bhat  * mostly).
10518b759b84SRafael J. Wysocki  *
1052f42a9813SRafael J. Wysocki  * The sysfs file /sys/power/disk provides an interface for selecting the
1053f42a9813SRafael J. Wysocki  * hibernation mode to use.  Reading from this file causes the available modes
105448580ab8SSrivatsa S. Bhat  * to be printed.  There are 3 modes that can be supported:
10558b759b84SRafael J. Wysocki  *
10568b759b84SRafael J. Wysocki  *	'platform'
10578b759b84SRafael J. Wysocki  *	'shutdown'
10588b759b84SRafael J. Wysocki  *	'reboot'
10598b759b84SRafael J. Wysocki  *
1060f42a9813SRafael J. Wysocki  * If a platform hibernation driver is in use, 'platform' will be supported
1061f42a9813SRafael J. Wysocki  * and will be used by default.  Otherwise, 'shutdown' will be used by default.
1062f42a9813SRafael J. Wysocki  * The selected option (i.e. the one corresponding to the current value of
1063f42a9813SRafael J. Wysocki  * hibernation_mode) is enclosed by a square bracket.
1064f42a9813SRafael J. Wysocki  *
1065f42a9813SRafael J. Wysocki  * To select a given hibernation mode it is necessary to write the mode's
1066f42a9813SRafael J. Wysocki  * string representation (as returned by reading from /sys/power/disk) back
1067f42a9813SRafael J. Wysocki  * into /sys/power/disk.
10688b759b84SRafael J. Wysocki  */
10698b759b84SRafael J. Wysocki 
disk_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)10708b759b84SRafael J. Wysocki static ssize_t disk_show(struct kobject *kobj, struct kobj_attribute *attr,
10718b759b84SRafael J. Wysocki 			 char *buf)
10728b759b84SRafael J. Wysocki {
10738b759b84SRafael J. Wysocki 	int i;
10748b759b84SRafael J. Wysocki 	char *start = buf;
10758b759b84SRafael J. Wysocki 
1076a6e15a39SKees Cook 	if (!hibernation_available())
1077a6e15a39SKees Cook 		return sprintf(buf, "[disabled]\n");
1078a6e15a39SKees Cook 
10798b759b84SRafael J. Wysocki 	for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) {
10808b759b84SRafael J. Wysocki 		if (!hibernation_modes[i])
10818b759b84SRafael J. Wysocki 			continue;
10828b759b84SRafael J. Wysocki 		switch (i) {
10838b759b84SRafael J. Wysocki 		case HIBERNATION_SHUTDOWN:
10848b759b84SRafael J. Wysocki 		case HIBERNATION_REBOOT:
108562c552ccSBojan Smojver #ifdef CONFIG_SUSPEND
108662c552ccSBojan Smojver 		case HIBERNATION_SUSPEND:
108762c552ccSBojan Smojver #endif
1088fe12c00dSChen Yu 		case HIBERNATION_TEST_RESUME:
10898b759b84SRafael J. Wysocki 			break;
10908b759b84SRafael J. Wysocki 		case HIBERNATION_PLATFORM:
10918b759b84SRafael J. Wysocki 			if (hibernation_ops)
10928b759b84SRafael J. Wysocki 				break;
10938b759b84SRafael J. Wysocki 			/* not a valid mode, continue with loop */
10948b759b84SRafael J. Wysocki 			continue;
10958b759b84SRafael J. Wysocki 		}
10968b759b84SRafael J. Wysocki 		if (i == hibernation_mode)
10978b759b84SRafael J. Wysocki 			buf += sprintf(buf, "[%s] ", hibernation_modes[i]);
10988b759b84SRafael J. Wysocki 		else
10998b759b84SRafael J. Wysocki 			buf += sprintf(buf, "%s ", hibernation_modes[i]);
11008b759b84SRafael J. Wysocki 	}
11018b759b84SRafael J. Wysocki 	buf += sprintf(buf, "\n");
11028b759b84SRafael J. Wysocki 	return buf-start;
11038b759b84SRafael J. Wysocki }
11048b759b84SRafael J. Wysocki 
disk_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)11058b759b84SRafael J. Wysocki static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr,
11068b759b84SRafael J. Wysocki 			  const char *buf, size_t n)
11078b759b84SRafael J. Wysocki {
11085950e5d5SPeter Zijlstra 	int mode = HIBERNATION_INVALID;
11095950e5d5SPeter Zijlstra 	unsigned int sleep_flags;
11108b759b84SRafael J. Wysocki 	int error = 0;
11118b759b84SRafael J. Wysocki 	int len;
11128b759b84SRafael J. Wysocki 	char *p;
11135950e5d5SPeter Zijlstra 	int i;
11148b759b84SRafael J. Wysocki 
1115a6e15a39SKees Cook 	if (!hibernation_available())
1116a6e15a39SKees Cook 		return -EPERM;
1117a6e15a39SKees Cook 
11188b759b84SRafael J. Wysocki 	p = memchr(buf, '\n', n);
11198b759b84SRafael J. Wysocki 	len = p ? p - buf : n;
11208b759b84SRafael J. Wysocki 
11215950e5d5SPeter Zijlstra 	sleep_flags = lock_system_sleep();
11228b759b84SRafael J. Wysocki 	for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) {
11238b759b84SRafael J. Wysocki 		if (len == strlen(hibernation_modes[i])
11248b759b84SRafael J. Wysocki 		    && !strncmp(buf, hibernation_modes[i], len)) {
11258b759b84SRafael J. Wysocki 			mode = i;
11268b759b84SRafael J. Wysocki 			break;
11278b759b84SRafael J. Wysocki 		}
11288b759b84SRafael J. Wysocki 	}
11298b759b84SRafael J. Wysocki 	if (mode != HIBERNATION_INVALID) {
11308b759b84SRafael J. Wysocki 		switch (mode) {
11318b759b84SRafael J. Wysocki 		case HIBERNATION_SHUTDOWN:
11328b759b84SRafael J. Wysocki 		case HIBERNATION_REBOOT:
113362c552ccSBojan Smojver #ifdef CONFIG_SUSPEND
113462c552ccSBojan Smojver 		case HIBERNATION_SUSPEND:
113562c552ccSBojan Smojver #endif
1136fe12c00dSChen Yu 		case HIBERNATION_TEST_RESUME:
11378b759b84SRafael J. Wysocki 			hibernation_mode = mode;
11388b759b84SRafael J. Wysocki 			break;
11398b759b84SRafael J. Wysocki 		case HIBERNATION_PLATFORM:
11408b759b84SRafael J. Wysocki 			if (hibernation_ops)
11418b759b84SRafael J. Wysocki 				hibernation_mode = mode;
11428b759b84SRafael J. Wysocki 			else
11438b759b84SRafael J. Wysocki 				error = -EINVAL;
11448b759b84SRafael J. Wysocki 		}
11458b759b84SRafael J. Wysocki 	} else
11468b759b84SRafael J. Wysocki 		error = -EINVAL;
11478b759b84SRafael J. Wysocki 
11488b759b84SRafael J. Wysocki 	if (!error)
11498d8b2441SRafael J. Wysocki 		pm_pr_dbg("Hibernation mode set to '%s'\n",
11508b759b84SRafael J. Wysocki 			       hibernation_modes[mode]);
11515950e5d5SPeter Zijlstra 	unlock_system_sleep(sleep_flags);
11528b759b84SRafael J. Wysocki 	return error ? error : n;
11538b759b84SRafael J. Wysocki }
11548b759b84SRafael J. Wysocki 
11558b759b84SRafael J. Wysocki power_attr(disk);
11568b759b84SRafael J. Wysocki 
resume_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)11578b759b84SRafael J. Wysocki static ssize_t resume_show(struct kobject *kobj, struct kobj_attribute *attr,
11588b759b84SRafael J. Wysocki 			   char *buf)
11598b759b84SRafael J. Wysocki {
11608b759b84SRafael J. Wysocki 	return sprintf(buf, "%d:%d\n", MAJOR(swsusp_resume_device),
11618b759b84SRafael J. Wysocki 		       MINOR(swsusp_resume_device));
11628b759b84SRafael J. Wysocki }
11638b759b84SRafael J. Wysocki 
resume_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)11648b759b84SRafael J. Wysocki static ssize_t resume_store(struct kobject *kobj, struct kobj_attribute *attr,
11658b759b84SRafael J. Wysocki 			    const char *buf, size_t n)
11668b759b84SRafael J. Wysocki {
11675950e5d5SPeter Zijlstra 	unsigned int sleep_flags;
1168421a5fa1SSebastian Capella 	int len = n;
1169421a5fa1SSebastian Capella 	char *name;
1170cf056a43SChristoph Hellwig 	dev_t dev;
1171cf056a43SChristoph Hellwig 	int error;
11728b759b84SRafael J. Wysocki 
1173cc89c63eSChristoph Hellwig 	if (!hibernation_available())
1174df2f7cdeSVlastimil Babka 		return n;
1175cc89c63eSChristoph Hellwig 
1176421a5fa1SSebastian Capella 	if (len && buf[len-1] == '\n')
1177421a5fa1SSebastian Capella 		len--;
1178421a5fa1SSebastian Capella 	name = kstrndup(buf, len, GFP_KERNEL);
1179421a5fa1SSebastian Capella 	if (!name)
1180421a5fa1SSebastian Capella 		return -ENOMEM;
11818b759b84SRafael J. Wysocki 
11821e8c813bSChristoph Hellwig 	error = lookup_bdev(name, &dev);
11831e8c813bSChristoph Hellwig 	if (error) {
11841e8c813bSChristoph Hellwig 		unsigned maj, min, offset;
11851e8c813bSChristoph Hellwig 		char *p, dummy;
11861e8c813bSChristoph Hellwig 
1187c9e4bf60SAzat Khuzhin 		error = 0;
11881e8c813bSChristoph Hellwig 		if (sscanf(name, "%u:%u%c", &maj, &min, &dummy) == 2 ||
11891e8c813bSChristoph Hellwig 		    sscanf(name, "%u:%u:%u:%c", &maj, &min, &offset,
11901e8c813bSChristoph Hellwig 				&dummy) == 3) {
11911e8c813bSChristoph Hellwig 			dev = MKDEV(maj, min);
11921e8c813bSChristoph Hellwig 			if (maj != MAJOR(dev) || min != MINOR(dev))
11931e8c813bSChristoph Hellwig 				error = -EINVAL;
11941e8c813bSChristoph Hellwig 		} else {
11951e8c813bSChristoph Hellwig 			dev = new_decode_dev(simple_strtoul(name, &p, 16));
11961e8c813bSChristoph Hellwig 			if (*p)
11971e8c813bSChristoph Hellwig 				error = -EINVAL;
11981e8c813bSChristoph Hellwig 		}
11991e8c813bSChristoph Hellwig 	}
1200421a5fa1SSebastian Capella 	kfree(name);
1201cf056a43SChristoph Hellwig 	if (error)
1202cf056a43SChristoph Hellwig 		return error;
12038b759b84SRafael J. Wysocki 
12045950e5d5SPeter Zijlstra 	sleep_flags = lock_system_sleep();
1205cf056a43SChristoph Hellwig 	swsusp_resume_device = dev;
12065950e5d5SPeter Zijlstra 	unlock_system_sleep(sleep_flags);
12075950e5d5SPeter Zijlstra 
12087a7b99bfSLuigi Semenzato 	pm_pr_dbg("Configured hibernation resume from disk to %u\n",
12097a7b99bfSLuigi Semenzato 		  swsusp_resume_device);
12108b759b84SRafael J. Wysocki 	noresume = 0;
12118b759b84SRafael J. Wysocki 	software_resume();
1212421a5fa1SSebastian Capella 	return n;
12138b759b84SRafael J. Wysocki }
12148b759b84SRafael J. Wysocki 
12158b759b84SRafael J. Wysocki power_attr(resume);
12168b759b84SRafael J. Wysocki 
resume_offset_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)121735506467SMario Limonciello static ssize_t resume_offset_show(struct kobject *kobj,
121835506467SMario Limonciello 				  struct kobj_attribute *attr, char *buf)
121935506467SMario Limonciello {
122035506467SMario Limonciello 	return sprintf(buf, "%llu\n", (unsigned long long)swsusp_resume_block);
122135506467SMario Limonciello }
122235506467SMario Limonciello 
resume_offset_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)122335506467SMario Limonciello static ssize_t resume_offset_store(struct kobject *kobj,
122435506467SMario Limonciello 				   struct kobj_attribute *attr, const char *buf,
122535506467SMario Limonciello 				   size_t n)
122635506467SMario Limonciello {
122735506467SMario Limonciello 	unsigned long long offset;
122835506467SMario Limonciello 	int rc;
122935506467SMario Limonciello 
123035506467SMario Limonciello 	rc = kstrtoull(buf, 0, &offset);
123135506467SMario Limonciello 	if (rc)
123235506467SMario Limonciello 		return rc;
123335506467SMario Limonciello 	swsusp_resume_block = offset;
123435506467SMario Limonciello 
123535506467SMario Limonciello 	return n;
123635506467SMario Limonciello }
123735506467SMario Limonciello 
123835506467SMario Limonciello power_attr(resume_offset);
123935506467SMario Limonciello 
image_size_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)12408b759b84SRafael J. Wysocki static ssize_t image_size_show(struct kobject *kobj, struct kobj_attribute *attr,
12418b759b84SRafael J. Wysocki 			       char *buf)
12428b759b84SRafael J. Wysocki {
12438b759b84SRafael J. Wysocki 	return sprintf(buf, "%lu\n", image_size);
12448b759b84SRafael J. Wysocki }
12458b759b84SRafael J. Wysocki 
image_size_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)12468b759b84SRafael J. Wysocki static ssize_t image_size_store(struct kobject *kobj, struct kobj_attribute *attr,
12478b759b84SRafael J. Wysocki 				const char *buf, size_t n)
12488b759b84SRafael J. Wysocki {
12498b759b84SRafael J. Wysocki 	unsigned long size;
12508b759b84SRafael J. Wysocki 
12518b759b84SRafael J. Wysocki 	if (sscanf(buf, "%lu", &size) == 1) {
12528b759b84SRafael J. Wysocki 		image_size = size;
12538b759b84SRafael J. Wysocki 		return n;
12548b759b84SRafael J. Wysocki 	}
12558b759b84SRafael J. Wysocki 
12568b759b84SRafael J. Wysocki 	return -EINVAL;
12578b759b84SRafael J. Wysocki }
12588b759b84SRafael J. Wysocki 
12598b759b84SRafael J. Wysocki power_attr(image_size);
12608b759b84SRafael J. Wysocki 
reserved_size_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)1261ddeb6487SRafael J. Wysocki static ssize_t reserved_size_show(struct kobject *kobj,
1262ddeb6487SRafael J. Wysocki 				  struct kobj_attribute *attr, char *buf)
1263ddeb6487SRafael J. Wysocki {
1264ddeb6487SRafael J. Wysocki 	return sprintf(buf, "%lu\n", reserved_size);
1265ddeb6487SRafael J. Wysocki }
1266ddeb6487SRafael J. Wysocki 
reserved_size_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)1267ddeb6487SRafael J. Wysocki static ssize_t reserved_size_store(struct kobject *kobj,
1268ddeb6487SRafael J. Wysocki 				   struct kobj_attribute *attr,
1269ddeb6487SRafael J. Wysocki 				   const char *buf, size_t n)
1270ddeb6487SRafael J. Wysocki {
1271ddeb6487SRafael J. Wysocki 	unsigned long size;
1272ddeb6487SRafael J. Wysocki 
1273ddeb6487SRafael J. Wysocki 	if (sscanf(buf, "%lu", &size) == 1) {
1274ddeb6487SRafael J. Wysocki 		reserved_size = size;
1275ddeb6487SRafael J. Wysocki 		return n;
1276ddeb6487SRafael J. Wysocki 	}
1277ddeb6487SRafael J. Wysocki 
1278ddeb6487SRafael J. Wysocki 	return -EINVAL;
1279ddeb6487SRafael J. Wysocki }
1280ddeb6487SRafael J. Wysocki 
1281ddeb6487SRafael J. Wysocki power_attr(reserved_size);
1282ddeb6487SRafael J. Wysocki 
12838b759b84SRafael J. Wysocki static struct attribute *g[] = {
12848b759b84SRafael J. Wysocki 	&disk_attr.attr,
128535506467SMario Limonciello 	&resume_offset_attr.attr,
12868b759b84SRafael J. Wysocki 	&resume_attr.attr,
12878b759b84SRafael J. Wysocki 	&image_size_attr.attr,
1288ddeb6487SRafael J. Wysocki 	&reserved_size_attr.attr,
12898b759b84SRafael J. Wysocki 	NULL,
12908b759b84SRafael J. Wysocki };
12918b759b84SRafael J. Wysocki 
12928b759b84SRafael J. Wysocki 
129359494fe2SArvind Yadav static const struct attribute_group attr_group = {
12948b759b84SRafael J. Wysocki 	.attrs = g,
12958b759b84SRafael J. Wysocki };
12968b759b84SRafael J. Wysocki 
12978b759b84SRafael J. Wysocki 
pm_disk_init(void)12988b759b84SRafael J. Wysocki static int __init pm_disk_init(void)
12998b759b84SRafael J. Wysocki {
13008b759b84SRafael J. Wysocki 	return sysfs_create_group(power_kobj, &attr_group);
13018b759b84SRafael J. Wysocki }
13028b759b84SRafael J. Wysocki 
13038b759b84SRafael J. Wysocki core_initcall(pm_disk_init);
13048b759b84SRafael J. Wysocki 
13058b759b84SRafael J. Wysocki 
resume_setup(char * str)13068b759b84SRafael J. Wysocki static int __init resume_setup(char *str)
13078b759b84SRafael J. Wysocki {
13088b759b84SRafael J. Wysocki 	if (noresume)
13098b759b84SRafael J. Wysocki 		return 1;
13108b759b84SRafael J. Wysocki 
13118b759b84SRafael J. Wysocki 	strncpy(resume_file, str, 255);
13128b759b84SRafael J. Wysocki 	return 1;
13138b759b84SRafael J. Wysocki }
13148b759b84SRafael J. Wysocki 
resume_offset_setup(char * str)13158b759b84SRafael J. Wysocki static int __init resume_offset_setup(char *str)
13168b759b84SRafael J. Wysocki {
13178b759b84SRafael J. Wysocki 	unsigned long long offset;
13188b759b84SRafael J. Wysocki 
13198b759b84SRafael J. Wysocki 	if (noresume)
13208b759b84SRafael J. Wysocki 		return 1;
13218b759b84SRafael J. Wysocki 
13228b759b84SRafael J. Wysocki 	if (sscanf(str, "%llu", &offset) == 1)
13238b759b84SRafael J. Wysocki 		swsusp_resume_block = offset;
13248b759b84SRafael J. Wysocki 
13258b759b84SRafael J. Wysocki 	return 1;
13268b759b84SRafael J. Wysocki }
13278b759b84SRafael J. Wysocki 
hibernate_setup(char * str)1328f996fc96SBojan Smojver static int __init hibernate_setup(char *str)
1329f996fc96SBojan Smojver {
13302f88e41aSRafael J. Wysocki 	if (!strncmp(str, "noresume", 8)) {
1331f996fc96SBojan Smojver 		noresume = 1;
13322f88e41aSRafael J. Wysocki 	} else if (!strncmp(str, "nocompress", 10)) {
1333f996fc96SBojan Smojver 		nocompress = 1;
13342f88e41aSRafael J. Wysocki 	} else if (!strncmp(str, "no", 2)) {
1335a6e15a39SKees Cook 		noresume = 1;
1336a6e15a39SKees Cook 		nohibernate = 1;
13370f5bf6d0SLaura Abbott 	} else if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX)
13384c0b6c10SRafael J. Wysocki 		   && !strncmp(str, "protect_image", 13)) {
13394c0b6c10SRafael J. Wysocki 		enable_restore_image_protection();
1340a6e15a39SKees Cook 	}
1341f996fc96SBojan Smojver 	return 1;
1342f996fc96SBojan Smojver }
1343f996fc96SBojan Smojver 
noresume_setup(char * str)13448b759b84SRafael J. Wysocki static int __init noresume_setup(char *str)
13458b759b84SRafael J. Wysocki {
13468b759b84SRafael J. Wysocki 	noresume = 1;
13478b759b84SRafael J. Wysocki 	return 1;
13488b759b84SRafael J. Wysocki }
13498b759b84SRafael J. Wysocki 
resumewait_setup(char * str)13506f8d7022SBarry Song static int __init resumewait_setup(char *str)
13516f8d7022SBarry Song {
13526f8d7022SBarry Song 	resume_wait = 1;
13536f8d7022SBarry Song 	return 1;
13546f8d7022SBarry Song }
13556f8d7022SBarry Song 
resumedelay_setup(char * str)1356f126f733SBarry Song static int __init resumedelay_setup(char *str)
1357f126f733SBarry Song {
1358f6514be5SDan Carpenter 	int rc = kstrtouint(str, 0, &resume_delay);
1359317cf7e5SFabian Frederick 
1360317cf7e5SFabian Frederick 	if (rc)
1361ba7ffcd4SRandy Dunlap 		pr_warn("resumedelay: bad option string '%s'\n", str);
1362f126f733SBarry Song 	return 1;
1363f126f733SBarry Song }
1364f126f733SBarry Song 
nohibernate_setup(char * str)1365a6e15a39SKees Cook static int __init nohibernate_setup(char *str)
1366a6e15a39SKees Cook {
1367a6e15a39SKees Cook 	noresume = 1;
1368a6e15a39SKees Cook 	nohibernate = 1;
1369a6e15a39SKees Cook 	return 1;
1370a6e15a39SKees Cook }
1371a6e15a39SKees Cook 
13728b759b84SRafael J. Wysocki __setup("noresume", noresume_setup);
13738b759b84SRafael J. Wysocki __setup("resume_offset=", resume_offset_setup);
13748b759b84SRafael J. Wysocki __setup("resume=", resume_setup);
1375f996fc96SBojan Smojver __setup("hibernate=", hibernate_setup);
13766f8d7022SBarry Song __setup("resumewait", resumewait_setup);
1377f126f733SBarry Song __setup("resumedelay=", resumedelay_setup);
1378a6e15a39SKees Cook __setup("nohibernate", nohibernate_setup);
1379