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