13d0407baSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 23d0407baSopenharmony_ci/* 33d0407baSopenharmony_ci * kernel/power/hibernate.c - Hibernation (a.k.a suspend-to-disk) support. 43d0407baSopenharmony_ci * 53d0407baSopenharmony_ci * Copyright (c) 2003 Patrick Mochel 63d0407baSopenharmony_ci * Copyright (c) 2003 Open Source Development Lab 73d0407baSopenharmony_ci * Copyright (c) 2004 Pavel Machek <pavel@ucw.cz> 83d0407baSopenharmony_ci * Copyright (c) 2009 Rafael J. Wysocki, Novell Inc. 93d0407baSopenharmony_ci * Copyright (C) 2012 Bojan Smojver <bojan@rexursive.com> 103d0407baSopenharmony_ci */ 113d0407baSopenharmony_ci 123d0407baSopenharmony_ci#define pr_fmt(fmt) "PM: hibernation: " fmt 133d0407baSopenharmony_ci 143d0407baSopenharmony_ci#include <linux/export.h> 153d0407baSopenharmony_ci#include <linux/suspend.h> 163d0407baSopenharmony_ci#include <linux/reboot.h> 173d0407baSopenharmony_ci#include <linux/string.h> 183d0407baSopenharmony_ci#include <linux/device.h> 193d0407baSopenharmony_ci#include <linux/async.h> 203d0407baSopenharmony_ci#include <linux/delay.h> 213d0407baSopenharmony_ci#include <linux/fs.h> 223d0407baSopenharmony_ci#include <linux/mount.h> 233d0407baSopenharmony_ci#include <linux/pm.h> 243d0407baSopenharmony_ci#include <linux/nmi.h> 253d0407baSopenharmony_ci#include <linux/console.h> 263d0407baSopenharmony_ci#include <linux/cpu.h> 273d0407baSopenharmony_ci#include <linux/freezer.h> 283d0407baSopenharmony_ci#include <linux/gfp.h> 293d0407baSopenharmony_ci#include <linux/syscore_ops.h> 303d0407baSopenharmony_ci#include <linux/ctype.h> 313d0407baSopenharmony_ci#include <linux/genhd.h> 323d0407baSopenharmony_ci#include <linux/ktime.h> 333d0407baSopenharmony_ci#include <linux/security.h> 343d0407baSopenharmony_ci#include <trace/events/power.h> 353d0407baSopenharmony_ci 363d0407baSopenharmony_ci#include "power.h" 373d0407baSopenharmony_ci 383d0407baSopenharmony_ci#define HIBERNATE_TWO 2 393d0407baSopenharmony_ci#define HIBERNATE_EIGHT 8 403d0407baSopenharmony_ci#define HIBERNATE_TEN 10 413d0407baSopenharmony_ci#define HIBERNATE_THIRTEEN 13 423d0407baSopenharmony_ci#define HIBERNATE_ONEHUNDRED 100 433d0407baSopenharmony_ci#define HIBERNATE_TWOHUNDREDFIFTYFIVE 255 443d0407baSopenharmony_ci#define HIBERNATE_TWOHUNDREDFIFTYSIX 256 453d0407baSopenharmony_ci#define HIBERNATE_ONETHOUSAND 1000 463d0407baSopenharmony_ci#define HIBERNATE_ONETHOUSANDTWENTYFOUR 1024 473d0407baSopenharmony_ci#define HIBERNATE_FIVETHOUSAND 5000 483d0407baSopenharmony_ci 493d0407baSopenharmony_cistatic int nocompress; 503d0407baSopenharmony_cistatic int noresume; 513d0407baSopenharmony_cistatic int nohibernate; 523d0407baSopenharmony_cistatic int resume_wait; 533d0407baSopenharmony_cistatic unsigned int resume_delay; 543d0407baSopenharmony_cistatic char resume_file[HIBERNATE_TWOHUNDREDFIFTYSIX] = CONFIG_PM_STD_PARTITION; 553d0407baSopenharmony_cidev_t swsusp_resume_device; 563d0407baSopenharmony_cisector_t swsusp_resume_block; 573d0407baSopenharmony_ci__visible int in_suspend __nosavedata; 583d0407baSopenharmony_ci 593d0407baSopenharmony_cienum { 603d0407baSopenharmony_ci HIBERNATION_INVALID, 613d0407baSopenharmony_ci HIBERNATION_PLATFORM, 623d0407baSopenharmony_ci HIBERNATION_SHUTDOWN, 633d0407baSopenharmony_ci HIBERNATION_REBOOT, 643d0407baSopenharmony_ci#ifdef CONFIG_SUSPEND 653d0407baSopenharmony_ci HIBERNATION_SUSPEND, 663d0407baSopenharmony_ci#endif 673d0407baSopenharmony_ci HIBERNATION_TEST_RESUME, 683d0407baSopenharmony_ci /* keep last */ 693d0407baSopenharmony_ci __HIBERNATION_AFTER_LAST 703d0407baSopenharmony_ci}; 713d0407baSopenharmony_ci#define HIBERNATION_MAX (__HIBERNATION_AFTER_LAST - 1) 723d0407baSopenharmony_ci#define HIBERNATION_FIRST (HIBERNATION_INVALID + 1) 733d0407baSopenharmony_ci 743d0407baSopenharmony_cistatic int hibernation_mode = HIBERNATION_SHUTDOWN; 753d0407baSopenharmony_ci 763d0407baSopenharmony_cibool freezer_test_done; 773d0407baSopenharmony_ci 783d0407baSopenharmony_cistatic const struct platform_hibernation_ops *hibernation_ops; 793d0407baSopenharmony_ci 803d0407baSopenharmony_cistatic atomic_t hibernate_atomic = ATOMIC_INIT(1); 813d0407baSopenharmony_ci 823d0407baSopenharmony_cibool hibernate_acquire(void) 833d0407baSopenharmony_ci{ 843d0407baSopenharmony_ci return atomic_add_unless(&hibernate_atomic, -1, 0); 853d0407baSopenharmony_ci} 863d0407baSopenharmony_ci 873d0407baSopenharmony_civoid hibernate_release(void) 883d0407baSopenharmony_ci{ 893d0407baSopenharmony_ci atomic_inc(&hibernate_atomic); 903d0407baSopenharmony_ci} 913d0407baSopenharmony_ci 923d0407baSopenharmony_cibool hibernation_available(void) 933d0407baSopenharmony_ci{ 943d0407baSopenharmony_ci return nohibernate == 0 && !security_locked_down(LOCKDOWN_HIBERNATION); 953d0407baSopenharmony_ci} 963d0407baSopenharmony_ci 973d0407baSopenharmony_ci/** 983d0407baSopenharmony_ci * hibernation_set_ops - Set the global hibernate operations. 993d0407baSopenharmony_ci * @ops: Hibernation operations to use in subsequent hibernation transitions. 1003d0407baSopenharmony_ci */ 1013d0407baSopenharmony_civoid hibernation_set_ops(const struct platform_hibernation_ops *ops) 1023d0407baSopenharmony_ci{ 1033d0407baSopenharmony_ci if (ops && !(ops->begin && ops->end && ops->pre_snapshot && ops->prepare && ops->finish && ops->enter && 1043d0407baSopenharmony_ci ops->pre_restore && ops->restore_cleanup && ops->leave)) { 1053d0407baSopenharmony_ci WARN_ON(1); 1063d0407baSopenharmony_ci return; 1073d0407baSopenharmony_ci } 1083d0407baSopenharmony_ci lock_system_sleep(); 1093d0407baSopenharmony_ci hibernation_ops = ops; 1103d0407baSopenharmony_ci if (ops) { 1113d0407baSopenharmony_ci hibernation_mode = HIBERNATION_PLATFORM; 1123d0407baSopenharmony_ci } else if (hibernation_mode == HIBERNATION_PLATFORM) { 1133d0407baSopenharmony_ci hibernation_mode = HIBERNATION_SHUTDOWN; 1143d0407baSopenharmony_ci } 1153d0407baSopenharmony_ci 1163d0407baSopenharmony_ci unlock_system_sleep(); 1173d0407baSopenharmony_ci} 1183d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(hibernation_set_ops); 1193d0407baSopenharmony_ci 1203d0407baSopenharmony_cistatic bool entering_platform_hibernation; 1213d0407baSopenharmony_ci 1223d0407baSopenharmony_cibool system_entering_hibernation(void) 1233d0407baSopenharmony_ci{ 1243d0407baSopenharmony_ci return entering_platform_hibernation; 1253d0407baSopenharmony_ci} 1263d0407baSopenharmony_ciEXPORT_SYMBOL(system_entering_hibernation); 1273d0407baSopenharmony_ci 1283d0407baSopenharmony_ci#ifdef CONFIG_PM_DEBUG 1293d0407baSopenharmony_cistatic void hibernation_debug_sleep(void) 1303d0407baSopenharmony_ci{ 1313d0407baSopenharmony_ci pr_info("debug: Waiting for 5 seconds.\n"); 1323d0407baSopenharmony_ci mdelay(HIBERNATE_FIVETHOUSAND); 1333d0407baSopenharmony_ci} 1343d0407baSopenharmony_ci 1353d0407baSopenharmony_cistatic int hibernation_test(int level) 1363d0407baSopenharmony_ci{ 1373d0407baSopenharmony_ci if (pm_test_level == level) { 1383d0407baSopenharmony_ci hibernation_debug_sleep(); 1393d0407baSopenharmony_ci return 1; 1403d0407baSopenharmony_ci } 1413d0407baSopenharmony_ci return 0; 1423d0407baSopenharmony_ci} 1433d0407baSopenharmony_ci#else /* !CONFIG_PM_DEBUG */ 1443d0407baSopenharmony_cistatic int hibernation_test(int level) 1453d0407baSopenharmony_ci{ 1463d0407baSopenharmony_ci return 0; 1473d0407baSopenharmony_ci} 1483d0407baSopenharmony_ci#endif /* !CONFIG_PM_DEBUG */ 1493d0407baSopenharmony_ci 1503d0407baSopenharmony_ci/** 1513d0407baSopenharmony_ci * platform_begin - Call platform to start hibernation. 1523d0407baSopenharmony_ci * @platform_mode: Whether or not to use the platform driver. 1533d0407baSopenharmony_ci */ 1543d0407baSopenharmony_cistatic int platform_begin(int platform_mode) 1553d0407baSopenharmony_ci{ 1563d0407baSopenharmony_ci return (platform_mode && hibernation_ops) ? hibernation_ops->begin(PMSG_FREEZE) : 0; 1573d0407baSopenharmony_ci} 1583d0407baSopenharmony_ci 1593d0407baSopenharmony_ci/** 1603d0407baSopenharmony_ci * platform_end - Call platform to finish transition to the working state. 1613d0407baSopenharmony_ci * @platform_mode: Whether or not to use the platform driver. 1623d0407baSopenharmony_ci */ 1633d0407baSopenharmony_cistatic void platform_end(int platform_mode) 1643d0407baSopenharmony_ci{ 1653d0407baSopenharmony_ci if (platform_mode && hibernation_ops) { 1663d0407baSopenharmony_ci hibernation_ops->end(); 1673d0407baSopenharmony_ci } 1683d0407baSopenharmony_ci} 1693d0407baSopenharmony_ci 1703d0407baSopenharmony_ci/** 1713d0407baSopenharmony_ci * platform_pre_snapshot - Call platform to prepare the machine for hibernation. 1723d0407baSopenharmony_ci * @platform_mode: Whether or not to use the platform driver. 1733d0407baSopenharmony_ci * 1743d0407baSopenharmony_ci * Use the platform driver to prepare the system for creating a hibernate image, 1753d0407baSopenharmony_ci * if so configured, and return an error code if that fails. 1763d0407baSopenharmony_ci */ 1773d0407baSopenharmony_ci 1783d0407baSopenharmony_cistatic int platform_pre_snapshot(int platform_mode) 1793d0407baSopenharmony_ci{ 1803d0407baSopenharmony_ci return (platform_mode && hibernation_ops) ? hibernation_ops->pre_snapshot() : 0; 1813d0407baSopenharmony_ci} 1823d0407baSopenharmony_ci 1833d0407baSopenharmony_ci/** 1843d0407baSopenharmony_ci * platform_leave - Call platform to prepare a transition to the working state. 1853d0407baSopenharmony_ci * @platform_mode: Whether or not to use the platform driver. 1863d0407baSopenharmony_ci * 1873d0407baSopenharmony_ci * Use the platform driver prepare to prepare the machine for switching to the 1883d0407baSopenharmony_ci * normal mode of operation. 1893d0407baSopenharmony_ci * 1903d0407baSopenharmony_ci * This routine is called on one CPU with interrupts disabled. 1913d0407baSopenharmony_ci */ 1923d0407baSopenharmony_cistatic void platform_leave(int platform_mode) 1933d0407baSopenharmony_ci{ 1943d0407baSopenharmony_ci if (platform_mode && hibernation_ops) { 1953d0407baSopenharmony_ci hibernation_ops->leave(); 1963d0407baSopenharmony_ci } 1973d0407baSopenharmony_ci} 1983d0407baSopenharmony_ci 1993d0407baSopenharmony_ci/** 2003d0407baSopenharmony_ci * platform_finish - Call platform to switch the system to the working state. 2013d0407baSopenharmony_ci * @platform_mode: Whether or not to use the platform driver. 2023d0407baSopenharmony_ci * 2033d0407baSopenharmony_ci * Use the platform driver to switch the machine to the normal mode of 2043d0407baSopenharmony_ci * operation. 2053d0407baSopenharmony_ci * 2063d0407baSopenharmony_ci * This routine must be called after platform_prepare(). 2073d0407baSopenharmony_ci */ 2083d0407baSopenharmony_cistatic void platform_finish(int platform_mode) 2093d0407baSopenharmony_ci{ 2103d0407baSopenharmony_ci if (platform_mode && hibernation_ops) { 2113d0407baSopenharmony_ci hibernation_ops->finish(); 2123d0407baSopenharmony_ci } 2133d0407baSopenharmony_ci} 2143d0407baSopenharmony_ci 2153d0407baSopenharmony_ci/** 2163d0407baSopenharmony_ci * platform_pre_restore - Prepare for hibernate image restoration. 2173d0407baSopenharmony_ci * @platform_mode: Whether or not to use the platform driver. 2183d0407baSopenharmony_ci * 2193d0407baSopenharmony_ci * Use the platform driver to prepare the system for resume from a hibernation 2203d0407baSopenharmony_ci * image. 2213d0407baSopenharmony_ci * 2223d0407baSopenharmony_ci * If the restore fails after this function has been called, 2233d0407baSopenharmony_ci * platform_restore_cleanup() must be called. 2243d0407baSopenharmony_ci */ 2253d0407baSopenharmony_cistatic int platform_pre_restore(int platform_mode) 2263d0407baSopenharmony_ci{ 2273d0407baSopenharmony_ci return (platform_mode && hibernation_ops) ? hibernation_ops->pre_restore() : 0; 2283d0407baSopenharmony_ci} 2293d0407baSopenharmony_ci 2303d0407baSopenharmony_ci/** 2313d0407baSopenharmony_ci * platform_restore_cleanup - Switch to the working state after failing restore. 2323d0407baSopenharmony_ci * @platform_mode: Whether or not to use the platform driver. 2333d0407baSopenharmony_ci * 2343d0407baSopenharmony_ci * Use the platform driver to switch the system to the normal mode of operation 2353d0407baSopenharmony_ci * after a failing restore. 2363d0407baSopenharmony_ci * 2373d0407baSopenharmony_ci * If platform_pre_restore() has been called before the failing restore, this 2383d0407baSopenharmony_ci * function must be called too, regardless of the result of 2393d0407baSopenharmony_ci * platform_pre_restore(). 2403d0407baSopenharmony_ci */ 2413d0407baSopenharmony_cistatic void platform_restore_cleanup(int platform_mode) 2423d0407baSopenharmony_ci{ 2433d0407baSopenharmony_ci if (platform_mode && hibernation_ops) { 2443d0407baSopenharmony_ci hibernation_ops->restore_cleanup(); 2453d0407baSopenharmony_ci } 2463d0407baSopenharmony_ci} 2473d0407baSopenharmony_ci 2483d0407baSopenharmony_ci/** 2493d0407baSopenharmony_ci * platform_recover - Recover from a failure to suspend devices. 2503d0407baSopenharmony_ci * @platform_mode: Whether or not to use the platform driver. 2513d0407baSopenharmony_ci */ 2523d0407baSopenharmony_cistatic void platform_recover(int platform_mode) 2533d0407baSopenharmony_ci{ 2543d0407baSopenharmony_ci if (platform_mode && hibernation_ops && hibernation_ops->recover) { 2553d0407baSopenharmony_ci hibernation_ops->recover(); 2563d0407baSopenharmony_ci } 2573d0407baSopenharmony_ci} 2583d0407baSopenharmony_ci 2593d0407baSopenharmony_ci/** 2603d0407baSopenharmony_ci * swsusp_show_speed - Print time elapsed between two events during hibernation. 2613d0407baSopenharmony_ci * @start: Starting event. 2623d0407baSopenharmony_ci * @stop: Final event. 2633d0407baSopenharmony_ci * @nr_pages: Number of memory pages processed between @start and @stop. 2643d0407baSopenharmony_ci * @msg: Additional diagnostic message to print. 2653d0407baSopenharmony_ci */ 2663d0407baSopenharmony_civoid swsusp_show_speed(ktime_t start, ktime_t stop, unsigned nr_pages, char *msg) 2673d0407baSopenharmony_ci{ 2683d0407baSopenharmony_ci ktime_t diff; 2693d0407baSopenharmony_ci u64 elapsed_centisecs64; 2703d0407baSopenharmony_ci unsigned int centisecs; 2713d0407baSopenharmony_ci unsigned int k; 2723d0407baSopenharmony_ci unsigned int kps; 2733d0407baSopenharmony_ci 2743d0407baSopenharmony_ci diff = ktime_sub(stop, start); 2753d0407baSopenharmony_ci elapsed_centisecs64 = ktime_divns(diff, HIBERNATE_TEN * NSEC_PER_MSEC); 2763d0407baSopenharmony_ci centisecs = elapsed_centisecs64; 2773d0407baSopenharmony_ci if (centisecs == 0) { 2783d0407baSopenharmony_ci centisecs = 1; /* avoid div-by-zero */ 2793d0407baSopenharmony_ci } 2803d0407baSopenharmony_ci k = nr_pages * (PAGE_SIZE / HIBERNATE_ONETHOUSANDTWENTYFOUR); 2813d0407baSopenharmony_ci kps = (k * HIBERNATE_ONEHUNDRED) / centisecs; 2823d0407baSopenharmony_ci pr_info("%s %u kbytes in %u.%02u seconds (%u.%02u MB/s)\n", msg, k, centisecs / HIBERNATE_ONEHUNDRED, 2833d0407baSopenharmony_ci centisecs % HIBERNATE_ONEHUNDRED, kps / HIBERNATE_ONETHOUSAND, 2843d0407baSopenharmony_ci (kps % HIBERNATE_ONETHOUSAND) / HIBERNATE_TEN); 2853d0407baSopenharmony_ci} 2863d0407baSopenharmony_ci 2873d0407baSopenharmony_ci__weak int arch_resume_nosmt(void) 2883d0407baSopenharmony_ci{ 2893d0407baSopenharmony_ci return 0; 2903d0407baSopenharmony_ci} 2913d0407baSopenharmony_ci 2923d0407baSopenharmony_ci/** 2933d0407baSopenharmony_ci * create_image - Create a hibernation image. 2943d0407baSopenharmony_ci * @platform_mode: Whether or not to use the platform driver. 2953d0407baSopenharmony_ci * 2963d0407baSopenharmony_ci * Execute device drivers' "late" and "noirq" freeze callbacks, create a 2973d0407baSopenharmony_ci * hibernation image and run the drivers' "noirq" and "early" thaw callbacks. 2983d0407baSopenharmony_ci * 2993d0407baSopenharmony_ci * Control reappears in this routine after the subsequent restore. 3003d0407baSopenharmony_ci */ 3013d0407baSopenharmony_cistatic int create_image(int platform_mode) 3023d0407baSopenharmony_ci{ 3033d0407baSopenharmony_ci int error; 3043d0407baSopenharmony_ci 3053d0407baSopenharmony_ci error = dpm_suspend_end(PMSG_FREEZE); 3063d0407baSopenharmony_ci if (error) { 3073d0407baSopenharmony_ci pr_err("Some devices failed to power down, aborting\n"); 3083d0407baSopenharmony_ci return error; 3093d0407baSopenharmony_ci } 3103d0407baSopenharmony_ci 3113d0407baSopenharmony_ci error = platform_pre_snapshot(platform_mode); 3123d0407baSopenharmony_ci if (error || hibernation_test(TEST_PLATFORM)) { 3133d0407baSopenharmony_ci goto Platform_finish; 3143d0407baSopenharmony_ci } 3153d0407baSopenharmony_ci 3163d0407baSopenharmony_ci error = suspend_disable_secondary_cpus(); 3173d0407baSopenharmony_ci if (error || hibernation_test(TEST_CPUS)) { 3183d0407baSopenharmony_ci goto Enable_cpus; 3193d0407baSopenharmony_ci } 3203d0407baSopenharmony_ci 3213d0407baSopenharmony_ci local_irq_disable(); 3223d0407baSopenharmony_ci 3233d0407baSopenharmony_ci system_state = SYSTEM_SUSPEND; 3243d0407baSopenharmony_ci 3253d0407baSopenharmony_ci error = syscore_suspend(); 3263d0407baSopenharmony_ci if (error) { 3273d0407baSopenharmony_ci pr_err("Some system devices failed to power down, aborting\n"); 3283d0407baSopenharmony_ci goto Enable_irqs; 3293d0407baSopenharmony_ci } 3303d0407baSopenharmony_ci 3313d0407baSopenharmony_ci if (hibernation_test(TEST_CORE) || pm_wakeup_pending()) { 3323d0407baSopenharmony_ci goto Power_up; 3333d0407baSopenharmony_ci } 3343d0407baSopenharmony_ci 3353d0407baSopenharmony_ci in_suspend = 1; 3363d0407baSopenharmony_ci save_processor_state(); 3373d0407baSopenharmony_ci trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, true); 3383d0407baSopenharmony_ci error = swsusp_arch_suspend(); 3393d0407baSopenharmony_ci /* Restore control flow magically appears here */ 3403d0407baSopenharmony_ci restore_processor_state(); 3413d0407baSopenharmony_ci trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, false); 3423d0407baSopenharmony_ci if (error) { 3433d0407baSopenharmony_ci pr_err("Error %d creating image\n", error); 3443d0407baSopenharmony_ci } 3453d0407baSopenharmony_ci 3463d0407baSopenharmony_ci if (!in_suspend) { 3473d0407baSopenharmony_ci events_check_enabled = false; 3483d0407baSopenharmony_ci clear_or_poison_free_pages(); 3493d0407baSopenharmony_ci } 3503d0407baSopenharmony_ci 3513d0407baSopenharmony_ci platform_leave(platform_mode); 3523d0407baSopenharmony_ci 3533d0407baSopenharmony_ciPower_up: 3543d0407baSopenharmony_ci syscore_resume(); 3553d0407baSopenharmony_ci 3563d0407baSopenharmony_ciEnable_irqs: 3573d0407baSopenharmony_ci system_state = SYSTEM_RUNNING; 3583d0407baSopenharmony_ci local_irq_enable(); 3593d0407baSopenharmony_ci 3603d0407baSopenharmony_ciEnable_cpus: 3613d0407baSopenharmony_ci suspend_enable_secondary_cpus(); 3623d0407baSopenharmony_ci 3633d0407baSopenharmony_ci /* Allow architectures to do nosmt-specific post-resume dances */ 3643d0407baSopenharmony_ci if (!in_suspend) { 3653d0407baSopenharmony_ci error = arch_resume_nosmt(); 3663d0407baSopenharmony_ci } 3673d0407baSopenharmony_ci 3683d0407baSopenharmony_ciPlatform_finish: 3693d0407baSopenharmony_ci platform_finish(platform_mode); 3703d0407baSopenharmony_ci 3713d0407baSopenharmony_ci dpm_resume_start(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); 3723d0407baSopenharmony_ci 3733d0407baSopenharmony_ci return error; 3743d0407baSopenharmony_ci} 3753d0407baSopenharmony_ci 3763d0407baSopenharmony_ci/** 3773d0407baSopenharmony_ci * hibernation_snapshot - Quiesce devices and create a hibernation image. 3783d0407baSopenharmony_ci * @platform_mode: If set, use platform driver to prepare for the transition. 3793d0407baSopenharmony_ci * 3803d0407baSopenharmony_ci * This routine must be called with system_transition_mutex held. 3813d0407baSopenharmony_ci */ 3823d0407baSopenharmony_ciint hibernation_snapshot(int platform_mode) 3833d0407baSopenharmony_ci{ 3843d0407baSopenharmony_ci pm_message_t msg; 3853d0407baSopenharmony_ci int error; 3863d0407baSopenharmony_ci 3873d0407baSopenharmony_ci pm_suspend_clear_flags(); 3883d0407baSopenharmony_ci error = platform_begin(platform_mode); 3893d0407baSopenharmony_ci if (error) { 3903d0407baSopenharmony_ci goto Close; 3913d0407baSopenharmony_ci } 3923d0407baSopenharmony_ci 3933d0407baSopenharmony_ci /* Preallocate image memory before shutting down devices. */ 3943d0407baSopenharmony_ci error = hibernate_preallocate_memory(); 3953d0407baSopenharmony_ci if (error) { 3963d0407baSopenharmony_ci goto Close; 3973d0407baSopenharmony_ci } 3983d0407baSopenharmony_ci 3993d0407baSopenharmony_ci error = freeze_kernel_threads(); 4003d0407baSopenharmony_ci if (error) { 4013d0407baSopenharmony_ci goto Cleanup; 4023d0407baSopenharmony_ci } 4033d0407baSopenharmony_ci 4043d0407baSopenharmony_ci if (hibernation_test(TEST_FREEZER)) { 4053d0407baSopenharmony_ci /* 4063d0407baSopenharmony_ci * Indicate to the caller that we are returning due to a 4073d0407baSopenharmony_ci * successful freezer test. 4083d0407baSopenharmony_ci */ 4093d0407baSopenharmony_ci freezer_test_done = true; 4103d0407baSopenharmony_ci goto Thaw; 4113d0407baSopenharmony_ci } 4123d0407baSopenharmony_ci 4133d0407baSopenharmony_ci error = dpm_prepare(PMSG_FREEZE); 4143d0407baSopenharmony_ci if (error) { 4153d0407baSopenharmony_ci dpm_complete(PMSG_RECOVER); 4163d0407baSopenharmony_ci goto Thaw; 4173d0407baSopenharmony_ci } 4183d0407baSopenharmony_ci 4193d0407baSopenharmony_ci suspend_console(); 4203d0407baSopenharmony_ci pm_restrict_gfp_mask(); 4213d0407baSopenharmony_ci 4223d0407baSopenharmony_ci error = dpm_suspend(PMSG_FREEZE); 4233d0407baSopenharmony_ci if (error || hibernation_test(TEST_DEVICES)) { 4243d0407baSopenharmony_ci platform_recover(platform_mode); 4253d0407baSopenharmony_ci } else { 4263d0407baSopenharmony_ci error = create_image(platform_mode); 4273d0407baSopenharmony_ci } 4283d0407baSopenharmony_ci 4293d0407baSopenharmony_ci /* 4303d0407baSopenharmony_ci * In the case that we call create_image() above, the control 4313d0407baSopenharmony_ci * returns here (1) after the image has been created or the 4323d0407baSopenharmony_ci * image creation has failed and (2) after a successful restore. 4333d0407baSopenharmony_ci */ 4343d0407baSopenharmony_ci 4353d0407baSopenharmony_ci /* We may need to release the preallocated image pages here. */ 4363d0407baSopenharmony_ci if (error || !in_suspend) { 4373d0407baSopenharmony_ci swsusp_free(); 4383d0407baSopenharmony_ci } 4393d0407baSopenharmony_ci 4403d0407baSopenharmony_ci msg = in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE; 4413d0407baSopenharmony_ci dpm_resume(msg); 4423d0407baSopenharmony_ci 4433d0407baSopenharmony_ci if (error || !in_suspend) { 4443d0407baSopenharmony_ci pm_restore_gfp_mask(); 4453d0407baSopenharmony_ci } 4463d0407baSopenharmony_ci 4473d0407baSopenharmony_ci resume_console(); 4483d0407baSopenharmony_ci dpm_complete(msg); 4493d0407baSopenharmony_ci 4503d0407baSopenharmony_ciClose: 4513d0407baSopenharmony_ci platform_end(platform_mode); 4523d0407baSopenharmony_ci return error; 4533d0407baSopenharmony_ci 4543d0407baSopenharmony_ciThaw: 4553d0407baSopenharmony_ci thaw_kernel_threads(); 4563d0407baSopenharmony_ciCleanup: 4573d0407baSopenharmony_ci swsusp_free(); 4583d0407baSopenharmony_ci platform_end(platform_mode); 4593d0407baSopenharmony_ci return error; 4603d0407baSopenharmony_ci} 4613d0407baSopenharmony_ci 4623d0407baSopenharmony_ciint __weak hibernate_resume_nonboot_cpu_disable(void) 4633d0407baSopenharmony_ci{ 4643d0407baSopenharmony_ci return suspend_disable_secondary_cpus(); 4653d0407baSopenharmony_ci} 4663d0407baSopenharmony_ci 4673d0407baSopenharmony_ci/** 4683d0407baSopenharmony_ci * resume_target_kernel - Restore system state from a hibernation image. 4693d0407baSopenharmony_ci * @platform_mode: Whether or not to use the platform driver. 4703d0407baSopenharmony_ci * 4713d0407baSopenharmony_ci * Execute device drivers' "noirq" and "late" freeze callbacks, restore the 4723d0407baSopenharmony_ci * contents of highmem that have not been restored yet from the image and run 4733d0407baSopenharmony_ci * the low-level code that will restore the remaining contents of memory and 4743d0407baSopenharmony_ci * switch to the just restored target kernel. 4753d0407baSopenharmony_ci */ 4763d0407baSopenharmony_cistatic int resume_target_kernel(bool platform_mode) 4773d0407baSopenharmony_ci{ 4783d0407baSopenharmony_ci int error; 4793d0407baSopenharmony_ci 4803d0407baSopenharmony_ci error = dpm_suspend_end(PMSG_QUIESCE); 4813d0407baSopenharmony_ci if (error) { 4823d0407baSopenharmony_ci pr_err("Some devices failed to power down, aborting resume\n"); 4833d0407baSopenharmony_ci return error; 4843d0407baSopenharmony_ci } 4853d0407baSopenharmony_ci 4863d0407baSopenharmony_ci error = platform_pre_restore(platform_mode); 4873d0407baSopenharmony_ci if (error) { 4883d0407baSopenharmony_ci goto Cleanup; 4893d0407baSopenharmony_ci } 4903d0407baSopenharmony_ci 4913d0407baSopenharmony_ci error = hibernate_resume_nonboot_cpu_disable(); 4923d0407baSopenharmony_ci if (error) { 4933d0407baSopenharmony_ci goto Enable_cpus; 4943d0407baSopenharmony_ci } 4953d0407baSopenharmony_ci 4963d0407baSopenharmony_ci local_irq_disable(); 4973d0407baSopenharmony_ci system_state = SYSTEM_SUSPEND; 4983d0407baSopenharmony_ci 4993d0407baSopenharmony_ci error = syscore_suspend(); 5003d0407baSopenharmony_ci if (error) { 5013d0407baSopenharmony_ci goto Enable_irqs; 5023d0407baSopenharmony_ci } 5033d0407baSopenharmony_ci 5043d0407baSopenharmony_ci save_processor_state(); 5053d0407baSopenharmony_ci error = restore_highmem(); 5063d0407baSopenharmony_ci if (!error) { 5073d0407baSopenharmony_ci error = swsusp_arch_resume(); 5083d0407baSopenharmony_ci /* 5093d0407baSopenharmony_ci * The code below is only ever reached in case of a failure. 5103d0407baSopenharmony_ci * Otherwise, execution continues at the place where 5113d0407baSopenharmony_ci * swsusp_arch_suspend() was called. 5123d0407baSopenharmony_ci */ 5133d0407baSopenharmony_ci BUG_ON(!error); 5143d0407baSopenharmony_ci /* 5153d0407baSopenharmony_ci * This call to restore_highmem() reverts the changes made by 5163d0407baSopenharmony_ci * the previous one. 5173d0407baSopenharmony_ci */ 5183d0407baSopenharmony_ci restore_highmem(); 5193d0407baSopenharmony_ci } 5203d0407baSopenharmony_ci /* 5213d0407baSopenharmony_ci * The only reason why swsusp_arch_resume() can fail is memory being 5223d0407baSopenharmony_ci * very tight, so we have to free it as soon as we can to avoid 5233d0407baSopenharmony_ci * subsequent failures. 5243d0407baSopenharmony_ci */ 5253d0407baSopenharmony_ci swsusp_free(); 5263d0407baSopenharmony_ci restore_processor_state(); 5273d0407baSopenharmony_ci touch_softlockup_watchdog(); 5283d0407baSopenharmony_ci 5293d0407baSopenharmony_ci syscore_resume(); 5303d0407baSopenharmony_ci 5313d0407baSopenharmony_ciEnable_irqs: 5323d0407baSopenharmony_ci system_state = SYSTEM_RUNNING; 5333d0407baSopenharmony_ci local_irq_enable(); 5343d0407baSopenharmony_ci 5353d0407baSopenharmony_ciEnable_cpus: 5363d0407baSopenharmony_ci suspend_enable_secondary_cpus(); 5373d0407baSopenharmony_ci 5383d0407baSopenharmony_ciCleanup: 5393d0407baSopenharmony_ci platform_restore_cleanup(platform_mode); 5403d0407baSopenharmony_ci 5413d0407baSopenharmony_ci dpm_resume_start(PMSG_RECOVER); 5423d0407baSopenharmony_ci 5433d0407baSopenharmony_ci return error; 5443d0407baSopenharmony_ci} 5453d0407baSopenharmony_ci 5463d0407baSopenharmony_ci/** 5473d0407baSopenharmony_ci * hibernation_restore - Quiesce devices and restore from a hibernation image. 5483d0407baSopenharmony_ci * @platform_mode: If set, use platform driver to prepare for the transition. 5493d0407baSopenharmony_ci * 5503d0407baSopenharmony_ci * This routine must be called with system_transition_mutex held. If it is 5513d0407baSopenharmony_ci * successful, control reappears in the restored target kernel in 5523d0407baSopenharmony_ci * hibernation_snapshot(). 5533d0407baSopenharmony_ci */ 5543d0407baSopenharmony_ciint hibernation_restore(int platform_mode) 5553d0407baSopenharmony_ci{ 5563d0407baSopenharmony_ci int error; 5573d0407baSopenharmony_ci 5583d0407baSopenharmony_ci pm_prepare_console(); 5593d0407baSopenharmony_ci suspend_console(); 5603d0407baSopenharmony_ci pm_restrict_gfp_mask(); 5613d0407baSopenharmony_ci error = dpm_suspend_start(PMSG_QUIESCE); 5623d0407baSopenharmony_ci if (!error) { 5633d0407baSopenharmony_ci error = resume_target_kernel(platform_mode); 5643d0407baSopenharmony_ci /* 5653d0407baSopenharmony_ci * The above should either succeed and jump to the new kernel, 5663d0407baSopenharmony_ci * or return with an error. Otherwise things are just 5673d0407baSopenharmony_ci * undefined, so let's be paranoid. 5683d0407baSopenharmony_ci */ 5693d0407baSopenharmony_ci BUG_ON(!error); 5703d0407baSopenharmony_ci } 5713d0407baSopenharmony_ci dpm_resume_end(PMSG_RECOVER); 5723d0407baSopenharmony_ci pm_restore_gfp_mask(); 5733d0407baSopenharmony_ci resume_console(); 5743d0407baSopenharmony_ci pm_restore_console(); 5753d0407baSopenharmony_ci return error; 5763d0407baSopenharmony_ci} 5773d0407baSopenharmony_ci 5783d0407baSopenharmony_ci/** 5793d0407baSopenharmony_ci * hibernation_platform_enter - Power off the system using the platform driver. 5803d0407baSopenharmony_ci */ 5813d0407baSopenharmony_ciint hibernation_platform_enter(void) 5823d0407baSopenharmony_ci{ 5833d0407baSopenharmony_ci int error; 5843d0407baSopenharmony_ci 5853d0407baSopenharmony_ci if (!hibernation_ops) { 5863d0407baSopenharmony_ci return -ENOSYS; 5873d0407baSopenharmony_ci } 5883d0407baSopenharmony_ci 5893d0407baSopenharmony_ci /* 5903d0407baSopenharmony_ci * We have cancelled the power transition by running 5913d0407baSopenharmony_ci * hibernation_ops->finish() before saving the image, so we should let 5923d0407baSopenharmony_ci * the firmware know that we're going to enter the sleep state after all 5933d0407baSopenharmony_ci */ 5943d0407baSopenharmony_ci error = hibernation_ops->begin(PMSG_HIBERNATE); 5953d0407baSopenharmony_ci if (error) { 5963d0407baSopenharmony_ci goto Close; 5973d0407baSopenharmony_ci } 5983d0407baSopenharmony_ci 5993d0407baSopenharmony_ci entering_platform_hibernation = true; 6003d0407baSopenharmony_ci suspend_console(); 6013d0407baSopenharmony_ci error = dpm_suspend_start(PMSG_HIBERNATE); 6023d0407baSopenharmony_ci if (error) { 6033d0407baSopenharmony_ci if (hibernation_ops->recover) { 6043d0407baSopenharmony_ci hibernation_ops->recover(); 6053d0407baSopenharmony_ci } 6063d0407baSopenharmony_ci goto Resume_devices; 6073d0407baSopenharmony_ci } 6083d0407baSopenharmony_ci 6093d0407baSopenharmony_ci error = dpm_suspend_end(PMSG_HIBERNATE); 6103d0407baSopenharmony_ci if (error) { 6113d0407baSopenharmony_ci goto Resume_devices; 6123d0407baSopenharmony_ci } 6133d0407baSopenharmony_ci 6143d0407baSopenharmony_ci error = hibernation_ops->prepare(); 6153d0407baSopenharmony_ci if (error) { 6163d0407baSopenharmony_ci goto Platform_finish; 6173d0407baSopenharmony_ci } 6183d0407baSopenharmony_ci 6193d0407baSopenharmony_ci error = suspend_disable_secondary_cpus(); 6203d0407baSopenharmony_ci if (error) { 6213d0407baSopenharmony_ci goto Enable_cpus; 6223d0407baSopenharmony_ci } 6233d0407baSopenharmony_ci 6243d0407baSopenharmony_ci local_irq_disable(); 6253d0407baSopenharmony_ci system_state = SYSTEM_SUSPEND; 6263d0407baSopenharmony_ci syscore_suspend(); 6273d0407baSopenharmony_ci if (pm_wakeup_pending()) { 6283d0407baSopenharmony_ci error = -EAGAIN; 6293d0407baSopenharmony_ci goto Power_up; 6303d0407baSopenharmony_ci } 6313d0407baSopenharmony_ci 6323d0407baSopenharmony_ci hibernation_ops->enter(); 6333d0407baSopenharmony_ci /* We should never get here */ 6343d0407baSopenharmony_ci while (1) { 6353d0407baSopenharmony_ci ; 6363d0407baSopenharmony_ci } 6373d0407baSopenharmony_ci 6383d0407baSopenharmony_ciPower_up: 6393d0407baSopenharmony_ci syscore_resume(); 6403d0407baSopenharmony_ci system_state = SYSTEM_RUNNING; 6413d0407baSopenharmony_ci local_irq_enable(); 6423d0407baSopenharmony_ci 6433d0407baSopenharmony_ciEnable_cpus: 6443d0407baSopenharmony_ci suspend_enable_secondary_cpus(); 6453d0407baSopenharmony_ci 6463d0407baSopenharmony_ciPlatform_finish: 6473d0407baSopenharmony_ci hibernation_ops->finish(); 6483d0407baSopenharmony_ci 6493d0407baSopenharmony_ci dpm_resume_start(PMSG_RESTORE); 6503d0407baSopenharmony_ci 6513d0407baSopenharmony_ciResume_devices: 6523d0407baSopenharmony_ci entering_platform_hibernation = false; 6533d0407baSopenharmony_ci dpm_resume_end(PMSG_RESTORE); 6543d0407baSopenharmony_ci resume_console(); 6553d0407baSopenharmony_ci 6563d0407baSopenharmony_ciClose: 6573d0407baSopenharmony_ci hibernation_ops->end(); 6583d0407baSopenharmony_ci 6593d0407baSopenharmony_ci return error; 6603d0407baSopenharmony_ci} 6613d0407baSopenharmony_ci 6623d0407baSopenharmony_ci/** 6633d0407baSopenharmony_ci * power_down - Shut the machine down for hibernation. 6643d0407baSopenharmony_ci * 6653d0407baSopenharmony_ci * Use the platform driver, if configured, to put the system into the sleep 6663d0407baSopenharmony_ci * state corresponding to hibernation, or try to power it off or reboot, 6673d0407baSopenharmony_ci * depending on the value of hibernation_mode. 6683d0407baSopenharmony_ci */ 6693d0407baSopenharmony_cistatic void power_down(void) 6703d0407baSopenharmony_ci{ 6713d0407baSopenharmony_ci#ifdef CONFIG_SUSPEND 6723d0407baSopenharmony_ci int error; 6733d0407baSopenharmony_ci 6743d0407baSopenharmony_ci if (hibernation_mode == HIBERNATION_SUSPEND) { 6753d0407baSopenharmony_ci error = suspend_devices_and_enter(PM_SUSPEND_MEM); 6763d0407baSopenharmony_ci if (error) { 6773d0407baSopenharmony_ci hibernation_mode = hibernation_ops ? HIBERNATION_PLATFORM : HIBERNATION_SHUTDOWN; 6783d0407baSopenharmony_ci } else { 6793d0407baSopenharmony_ci /* Restore swap signature. */ 6803d0407baSopenharmony_ci error = swsusp_unmark(); 6813d0407baSopenharmony_ci if (error) { 6823d0407baSopenharmony_ci pr_err("Swap will be unusable! Try swapon -a.\n"); 6833d0407baSopenharmony_ci } 6843d0407baSopenharmony_ci 6853d0407baSopenharmony_ci return; 6863d0407baSopenharmony_ci } 6873d0407baSopenharmony_ci } 6883d0407baSopenharmony_ci#endif 6893d0407baSopenharmony_ci 6903d0407baSopenharmony_ci switch (hibernation_mode) { 6913d0407baSopenharmony_ci case HIBERNATION_REBOOT: 6923d0407baSopenharmony_ci kernel_restart(NULL); 6933d0407baSopenharmony_ci break; 6943d0407baSopenharmony_ci case HIBERNATION_PLATFORM: 6953d0407baSopenharmony_ci hibernation_platform_enter(); 6963d0407baSopenharmony_ci fallthrough; 6973d0407baSopenharmony_ci case HIBERNATION_SHUTDOWN: 6983d0407baSopenharmony_ci if (pm_power_off) { 6993d0407baSopenharmony_ci kernel_power_off(); 7003d0407baSopenharmony_ci } 7013d0407baSopenharmony_ci break; 7023d0407baSopenharmony_ci } 7033d0407baSopenharmony_ci kernel_halt(); 7043d0407baSopenharmony_ci /* 7053d0407baSopenharmony_ci * Valid image is on the disk, if we continue we risk serious data 7063d0407baSopenharmony_ci * corruption after resume. 7073d0407baSopenharmony_ci */ 7083d0407baSopenharmony_ci pr_crit("Power down manually\n"); 7093d0407baSopenharmony_ci while (1) { 7103d0407baSopenharmony_ci cpu_relax(); 7113d0407baSopenharmony_ci } 7123d0407baSopenharmony_ci} 7133d0407baSopenharmony_ci 7143d0407baSopenharmony_cistatic int load_image_and_restore(void) 7153d0407baSopenharmony_ci{ 7163d0407baSopenharmony_ci int error; 7173d0407baSopenharmony_ci unsigned int flags; 7183d0407baSopenharmony_ci 7193d0407baSopenharmony_ci pm_pr_dbg("Loading hibernation image.\n"); 7203d0407baSopenharmony_ci 7213d0407baSopenharmony_ci lock_device_hotplug(); 7223d0407baSopenharmony_ci error = create_basic_memory_bitmaps(); 7233d0407baSopenharmony_ci if (error) { 7243d0407baSopenharmony_ci goto Unlock; 7253d0407baSopenharmony_ci } 7263d0407baSopenharmony_ci 7273d0407baSopenharmony_ci error = swsusp_read(&flags); 7283d0407baSopenharmony_ci swsusp_close(FMODE_READ | FMODE_EXCL); 7293d0407baSopenharmony_ci if (!error) { 7303d0407baSopenharmony_ci error = hibernation_restore(flags & SF_PLATFORM_MODE); 7313d0407baSopenharmony_ci } 7323d0407baSopenharmony_ci 7333d0407baSopenharmony_ci pr_err("Failed to load image, recovering.\n"); 7343d0407baSopenharmony_ci swsusp_free(); 7353d0407baSopenharmony_ci free_basic_memory_bitmaps(); 7363d0407baSopenharmony_ciUnlock: 7373d0407baSopenharmony_ci unlock_device_hotplug(); 7383d0407baSopenharmony_ci 7393d0407baSopenharmony_ci return error; 7403d0407baSopenharmony_ci} 7413d0407baSopenharmony_ci 7423d0407baSopenharmony_ci/** 7433d0407baSopenharmony_ci * hibernate - Carry out system hibernation, including saving the image. 7443d0407baSopenharmony_ci */ 7453d0407baSopenharmony_ciint hibernate(void) 7463d0407baSopenharmony_ci{ 7473d0407baSopenharmony_ci bool snapshot_test = false; 7483d0407baSopenharmony_ci int error; 7493d0407baSopenharmony_ci 7503d0407baSopenharmony_ci if (!hibernation_available()) { 7513d0407baSopenharmony_ci pm_pr_dbg("Hibernation not available.\n"); 7523d0407baSopenharmony_ci return -EPERM; 7533d0407baSopenharmony_ci } 7543d0407baSopenharmony_ci 7553d0407baSopenharmony_ci lock_system_sleep(); 7563d0407baSopenharmony_ci /* The snapshot device should not be opened while we're running */ 7573d0407baSopenharmony_ci if (!hibernate_acquire()) { 7583d0407baSopenharmony_ci error = -EBUSY; 7593d0407baSopenharmony_ci goto Unlock; 7603d0407baSopenharmony_ci } 7613d0407baSopenharmony_ci 7623d0407baSopenharmony_ci pr_info("hibernation entry\n"); 7633d0407baSopenharmony_ci pm_prepare_console(); 7643d0407baSopenharmony_ci error = pm_notifier_call_chain_robust(PM_HIBERNATION_PREPARE, PM_POST_HIBERNATION); 7653d0407baSopenharmony_ci if (error) { 7663d0407baSopenharmony_ci goto Restore; 7673d0407baSopenharmony_ci } 7683d0407baSopenharmony_ci 7693d0407baSopenharmony_ci ksys_sync_helper(); 7703d0407baSopenharmony_ci 7713d0407baSopenharmony_ci error = freeze_processes(); 7723d0407baSopenharmony_ci if (error) { 7733d0407baSopenharmony_ci goto Exit; 7743d0407baSopenharmony_ci } 7753d0407baSopenharmony_ci 7763d0407baSopenharmony_ci lock_device_hotplug(); 7773d0407baSopenharmony_ci /* Allocate memory management structures */ 7783d0407baSopenharmony_ci error = create_basic_memory_bitmaps(); 7793d0407baSopenharmony_ci if (error) { 7803d0407baSopenharmony_ci goto Thaw; 7813d0407baSopenharmony_ci } 7823d0407baSopenharmony_ci 7833d0407baSopenharmony_ci error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM); 7843d0407baSopenharmony_ci if (error || freezer_test_done) { 7853d0407baSopenharmony_ci goto Free_bitmaps; 7863d0407baSopenharmony_ci } 7873d0407baSopenharmony_ci 7883d0407baSopenharmony_ci if (in_suspend) { 7893d0407baSopenharmony_ci unsigned int flags = 0; 7903d0407baSopenharmony_ci 7913d0407baSopenharmony_ci if (hibernation_mode == HIBERNATION_PLATFORM) { 7923d0407baSopenharmony_ci flags |= SF_PLATFORM_MODE; 7933d0407baSopenharmony_ci } 7943d0407baSopenharmony_ci if (nocompress) { 7953d0407baSopenharmony_ci flags |= SF_NOCOMPRESS_MODE; 7963d0407baSopenharmony_ci } else { 7973d0407baSopenharmony_ci flags |= SF_CRC32_MODE; 7983d0407baSopenharmony_ci } 7993d0407baSopenharmony_ci 8003d0407baSopenharmony_ci pm_pr_dbg("Writing hibernation image.\n"); 8013d0407baSopenharmony_ci error = swsusp_write(flags); 8023d0407baSopenharmony_ci swsusp_free(); 8033d0407baSopenharmony_ci if (!error) { 8043d0407baSopenharmony_ci if (hibernation_mode == HIBERNATION_TEST_RESUME) { 8053d0407baSopenharmony_ci snapshot_test = true; 8063d0407baSopenharmony_ci } else { 8073d0407baSopenharmony_ci power_down(); 8083d0407baSopenharmony_ci } 8093d0407baSopenharmony_ci } 8103d0407baSopenharmony_ci in_suspend = 0; 8113d0407baSopenharmony_ci pm_restore_gfp_mask(); 8123d0407baSopenharmony_ci } else { 8133d0407baSopenharmony_ci pm_pr_dbg("Hibernation image restored successfully.\n"); 8143d0407baSopenharmony_ci } 8153d0407baSopenharmony_ci 8163d0407baSopenharmony_ciFree_bitmaps: 8173d0407baSopenharmony_ci free_basic_memory_bitmaps(); 8183d0407baSopenharmony_ciThaw: 8193d0407baSopenharmony_ci unlock_device_hotplug(); 8203d0407baSopenharmony_ci if (snapshot_test) { 8213d0407baSopenharmony_ci pm_pr_dbg("Checking hibernation image\n"); 8223d0407baSopenharmony_ci error = swsusp_check(); 8233d0407baSopenharmony_ci if (!error) { 8243d0407baSopenharmony_ci error = load_image_and_restore(); 8253d0407baSopenharmony_ci } 8263d0407baSopenharmony_ci } 8273d0407baSopenharmony_ci thaw_processes(); 8283d0407baSopenharmony_ci 8293d0407baSopenharmony_ci /* Don't bother checking whether freezer_test_done is true */ 8303d0407baSopenharmony_ci freezer_test_done = false; 8313d0407baSopenharmony_ciExit: 8323d0407baSopenharmony_ci pm_notifier_call_chain(PM_POST_HIBERNATION); 8333d0407baSopenharmony_ciRestore: 8343d0407baSopenharmony_ci pm_restore_console(); 8353d0407baSopenharmony_ci hibernate_release(); 8363d0407baSopenharmony_ciUnlock: 8373d0407baSopenharmony_ci unlock_system_sleep(); 8383d0407baSopenharmony_ci pr_info("hibernation exit\n"); 8393d0407baSopenharmony_ci 8403d0407baSopenharmony_ci return error; 8413d0407baSopenharmony_ci} 8423d0407baSopenharmony_ci 8433d0407baSopenharmony_ci/** 8443d0407baSopenharmony_ci * hibernate_quiet_exec - Execute a function with all devices frozen. 8453d0407baSopenharmony_ci * @func: Function to execute. 8463d0407baSopenharmony_ci * @data: Data pointer to pass to @func. 8473d0407baSopenharmony_ci * 8483d0407baSopenharmony_ci * Return the @func return value or an error code if it cannot be executed. 8493d0407baSopenharmony_ci */ 8503d0407baSopenharmony_ciint hibernate_quiet_exec(int (*func)(void *data), void *data) 8513d0407baSopenharmony_ci{ 8523d0407baSopenharmony_ci int error; 8533d0407baSopenharmony_ci 8543d0407baSopenharmony_ci lock_system_sleep(); 8553d0407baSopenharmony_ci 8563d0407baSopenharmony_ci if (!hibernate_acquire()) { 8573d0407baSopenharmony_ci error = -EBUSY; 8583d0407baSopenharmony_ci goto unlock; 8593d0407baSopenharmony_ci } 8603d0407baSopenharmony_ci 8613d0407baSopenharmony_ci pm_prepare_console(); 8623d0407baSopenharmony_ci 8633d0407baSopenharmony_ci error = pm_notifier_call_chain_robust(PM_HIBERNATION_PREPARE, PM_POST_HIBERNATION); 8643d0407baSopenharmony_ci if (error) { 8653d0407baSopenharmony_ci goto restore; 8663d0407baSopenharmony_ci } 8673d0407baSopenharmony_ci 8683d0407baSopenharmony_ci error = freeze_processes(); 8693d0407baSopenharmony_ci if (error) { 8703d0407baSopenharmony_ci goto exit; 8713d0407baSopenharmony_ci } 8723d0407baSopenharmony_ci 8733d0407baSopenharmony_ci lock_device_hotplug(); 8743d0407baSopenharmony_ci 8753d0407baSopenharmony_ci pm_suspend_clear_flags(); 8763d0407baSopenharmony_ci 8773d0407baSopenharmony_ci error = platform_begin(true); 8783d0407baSopenharmony_ci if (error) { 8793d0407baSopenharmony_ci goto thaw; 8803d0407baSopenharmony_ci } 8813d0407baSopenharmony_ci 8823d0407baSopenharmony_ci error = freeze_kernel_threads(); 8833d0407baSopenharmony_ci if (error) { 8843d0407baSopenharmony_ci goto thaw; 8853d0407baSopenharmony_ci } 8863d0407baSopenharmony_ci 8873d0407baSopenharmony_ci error = dpm_prepare(PMSG_FREEZE); 8883d0407baSopenharmony_ci if (error) { 8893d0407baSopenharmony_ci goto dpm_complete; 8903d0407baSopenharmony_ci } 8913d0407baSopenharmony_ci 8923d0407baSopenharmony_ci suspend_console(); 8933d0407baSopenharmony_ci 8943d0407baSopenharmony_ci error = dpm_suspend(PMSG_FREEZE); 8953d0407baSopenharmony_ci if (error) { 8963d0407baSopenharmony_ci goto dpm_resume; 8973d0407baSopenharmony_ci } 8983d0407baSopenharmony_ci 8993d0407baSopenharmony_ci error = dpm_suspend_end(PMSG_FREEZE); 9003d0407baSopenharmony_ci if (error) { 9013d0407baSopenharmony_ci goto dpm_resume; 9023d0407baSopenharmony_ci } 9033d0407baSopenharmony_ci 9043d0407baSopenharmony_ci error = platform_pre_snapshot(true); 9053d0407baSopenharmony_ci if (error) { 9063d0407baSopenharmony_ci goto skip; 9073d0407baSopenharmony_ci } 9083d0407baSopenharmony_ci 9093d0407baSopenharmony_ci error = func(data); 9103d0407baSopenharmony_ci 9113d0407baSopenharmony_ciskip: 9123d0407baSopenharmony_ci platform_finish(true); 9133d0407baSopenharmony_ci 9143d0407baSopenharmony_ci dpm_resume_start(PMSG_THAW); 9153d0407baSopenharmony_ci 9163d0407baSopenharmony_cidpm_resume: 9173d0407baSopenharmony_ci dpm_resume(PMSG_THAW); 9183d0407baSopenharmony_ci 9193d0407baSopenharmony_ci resume_console(); 9203d0407baSopenharmony_ci 9213d0407baSopenharmony_cidpm_complete: 9223d0407baSopenharmony_ci dpm_complete(PMSG_THAW); 9233d0407baSopenharmony_ci 9243d0407baSopenharmony_ci thaw_kernel_threads(); 9253d0407baSopenharmony_ci 9263d0407baSopenharmony_cithaw: 9273d0407baSopenharmony_ci platform_end(true); 9283d0407baSopenharmony_ci 9293d0407baSopenharmony_ci unlock_device_hotplug(); 9303d0407baSopenharmony_ci 9313d0407baSopenharmony_ci thaw_processes(); 9323d0407baSopenharmony_ci 9333d0407baSopenharmony_ciexit: 9343d0407baSopenharmony_ci pm_notifier_call_chain(PM_POST_HIBERNATION); 9353d0407baSopenharmony_ci 9363d0407baSopenharmony_cirestore: 9373d0407baSopenharmony_ci pm_restore_console(); 9383d0407baSopenharmony_ci 9393d0407baSopenharmony_ci hibernate_release(); 9403d0407baSopenharmony_ci 9413d0407baSopenharmony_ciunlock: 9423d0407baSopenharmony_ci unlock_system_sleep(); 9433d0407baSopenharmony_ci 9443d0407baSopenharmony_ci return error; 9453d0407baSopenharmony_ci} 9463d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(hibernate_quiet_exec); 9473d0407baSopenharmony_ci 9483d0407baSopenharmony_ci/** 9493d0407baSopenharmony_ci * software_resume - Resume from a saved hibernation image. 9503d0407baSopenharmony_ci * 9513d0407baSopenharmony_ci * This routine is called as a late initcall, when all devices have been 9523d0407baSopenharmony_ci * discovered and initialized already. 9533d0407baSopenharmony_ci * 9543d0407baSopenharmony_ci * The image reading code is called to see if there is a hibernation image 9553d0407baSopenharmony_ci * available for reading. If that is the case, devices are quiesced and the 9563d0407baSopenharmony_ci * contents of memory is restored from the saved image. 9573d0407baSopenharmony_ci * 9583d0407baSopenharmony_ci * If this is successful, control reappears in the restored target kernel in 9593d0407baSopenharmony_ci * hibernation_snapshot() which returns to hibernate(). Otherwise, the routine 9603d0407baSopenharmony_ci * attempts to recover gracefully and make the kernel return to the normal mode 9613d0407baSopenharmony_ci * of operation. 9623d0407baSopenharmony_ci */ 9633d0407baSopenharmony_cistatic int software_resume(void) 9643d0407baSopenharmony_ci{ 9653d0407baSopenharmony_ci int error; 9663d0407baSopenharmony_ci 9673d0407baSopenharmony_ci /* 9683d0407baSopenharmony_ci * If the user said "noresume".. bail out early. 9693d0407baSopenharmony_ci */ 9703d0407baSopenharmony_ci if (noresume || !hibernation_available()) { 9713d0407baSopenharmony_ci return 0; 9723d0407baSopenharmony_ci } 9733d0407baSopenharmony_ci 9743d0407baSopenharmony_ci /* 9753d0407baSopenharmony_ci * name_to_dev_t() below takes a sysfs buffer mutex when sysfs 9763d0407baSopenharmony_ci * is configured into the kernel. Since the regular hibernate 9773d0407baSopenharmony_ci * trigger path is via sysfs which takes a buffer mutex before 9783d0407baSopenharmony_ci * calling hibernate functions (which take system_transition_mutex) 9793d0407baSopenharmony_ci * this can cause lockdep to complain about a possible ABBA deadlock 9803d0407baSopenharmony_ci * which cannot happen since we're in the boot code here and 9813d0407baSopenharmony_ci * sysfs can't be invoked yet. Therefore, we use a subclass 9823d0407baSopenharmony_ci * here to avoid lockdep complaining. 9833d0407baSopenharmony_ci */ 9843d0407baSopenharmony_ci mutex_lock_nested(&system_transition_mutex, SINGLE_DEPTH_NESTING); 9853d0407baSopenharmony_ci 9863d0407baSopenharmony_ci if (swsusp_resume_device) { 9873d0407baSopenharmony_ci goto Check_image; 9883d0407baSopenharmony_ci } 9893d0407baSopenharmony_ci 9903d0407baSopenharmony_ci if (!strlen(resume_file)) { 9913d0407baSopenharmony_ci error = -ENOENT; 9923d0407baSopenharmony_ci goto Unlock; 9933d0407baSopenharmony_ci } 9943d0407baSopenharmony_ci 9953d0407baSopenharmony_ci pm_pr_dbg("Checking hibernation image partition %s\n", resume_file); 9963d0407baSopenharmony_ci 9973d0407baSopenharmony_ci if (resume_delay) { 9983d0407baSopenharmony_ci pr_info("Waiting %dsec before reading resume device ...\n", resume_delay); 9993d0407baSopenharmony_ci ssleep(resume_delay); 10003d0407baSopenharmony_ci } 10013d0407baSopenharmony_ci 10023d0407baSopenharmony_ci /* Check if the device is there */ 10033d0407baSopenharmony_ci swsusp_resume_device = name_to_dev_t(resume_file); 10043d0407baSopenharmony_ci if (!swsusp_resume_device) { 10053d0407baSopenharmony_ci /* 10063d0407baSopenharmony_ci * Some device discovery might still be in progress; we need 10073d0407baSopenharmony_ci * to wait for this to finish. 10083d0407baSopenharmony_ci */ 10093d0407baSopenharmony_ci wait_for_device_probe(); 10103d0407baSopenharmony_ci 10113d0407baSopenharmony_ci if (resume_wait) { 10123d0407baSopenharmony_ci while ((swsusp_resume_device = name_to_dev_t(resume_file)) == 0) { 10133d0407baSopenharmony_ci msleep(HIBERNATE_TEN); 10143d0407baSopenharmony_ci } 10153d0407baSopenharmony_ci async_synchronize_full(); 10163d0407baSopenharmony_ci } 10173d0407baSopenharmony_ci 10183d0407baSopenharmony_ci swsusp_resume_device = name_to_dev_t(resume_file); 10193d0407baSopenharmony_ci if (!swsusp_resume_device) { 10203d0407baSopenharmony_ci error = -ENODEV; 10213d0407baSopenharmony_ci goto Unlock; 10223d0407baSopenharmony_ci } 10233d0407baSopenharmony_ci } 10243d0407baSopenharmony_ci 10253d0407baSopenharmony_ciCheck_image: 10263d0407baSopenharmony_ci pm_pr_dbg("Hibernation image partition %d:%d present\n", MAJOR(swsusp_resume_device), MINOR(swsusp_resume_device)); 10273d0407baSopenharmony_ci 10283d0407baSopenharmony_ci pm_pr_dbg("Looking for hibernation image.\n"); 10293d0407baSopenharmony_ci error = swsusp_check(); 10303d0407baSopenharmony_ci if (error) { 10313d0407baSopenharmony_ci goto Unlock; 10323d0407baSopenharmony_ci } 10333d0407baSopenharmony_ci 10343d0407baSopenharmony_ci /* The snapshot device should not be opened while we're running */ 10353d0407baSopenharmony_ci if (!hibernate_acquire()) { 10363d0407baSopenharmony_ci error = -EBUSY; 10373d0407baSopenharmony_ci swsusp_close(FMODE_READ | FMODE_EXCL); 10383d0407baSopenharmony_ci goto Unlock; 10393d0407baSopenharmony_ci } 10403d0407baSopenharmony_ci 10413d0407baSopenharmony_ci pr_info("resume from hibernation\n"); 10423d0407baSopenharmony_ci pm_prepare_console(); 10433d0407baSopenharmony_ci error = pm_notifier_call_chain_robust(PM_RESTORE_PREPARE, PM_POST_RESTORE); 10443d0407baSopenharmony_ci if (error) { 10453d0407baSopenharmony_ci goto Restore; 10463d0407baSopenharmony_ci } 10473d0407baSopenharmony_ci 10483d0407baSopenharmony_ci pm_pr_dbg("Preparing processes for hibernation restore.\n"); 10493d0407baSopenharmony_ci error = freeze_processes(); 10503d0407baSopenharmony_ci if (error) { 10513d0407baSopenharmony_ci swsusp_close(FMODE_READ | FMODE_EXCL); 10523d0407baSopenharmony_ci goto Finish; 10533d0407baSopenharmony_ci } 10543d0407baSopenharmony_ci 10553d0407baSopenharmony_ci error = freeze_kernel_threads(); 10563d0407baSopenharmony_ci if (error) { 10573d0407baSopenharmony_ci thaw_processes(); 10583d0407baSopenharmony_ci swsusp_close(FMODE_READ | FMODE_EXCL); 10593d0407baSopenharmony_ci goto Finish; 10603d0407baSopenharmony_ci } 10613d0407baSopenharmony_ci 10623d0407baSopenharmony_ci error = load_image_and_restore(); 10633d0407baSopenharmony_ci thaw_processes(); 10643d0407baSopenharmony_ciFinish: 10653d0407baSopenharmony_ci pm_notifier_call_chain(PM_POST_RESTORE); 10663d0407baSopenharmony_ciRestore: 10673d0407baSopenharmony_ci pm_restore_console(); 10683d0407baSopenharmony_ci pr_info("resume failed (%d)\n", error); 10693d0407baSopenharmony_ci hibernate_release(); 10703d0407baSopenharmony_ci /* For success case, the suspend path will release the lock */ 10713d0407baSopenharmony_ciUnlock: 10723d0407baSopenharmony_ci mutex_unlock(&system_transition_mutex); 10733d0407baSopenharmony_ci pm_pr_dbg("Hibernation image not present or could not be loaded.\n"); 10743d0407baSopenharmony_ci return error; 10753d0407baSopenharmony_ci} 10763d0407baSopenharmony_ci 10773d0407baSopenharmony_cilate_initcall_sync(software_resume); 10783d0407baSopenharmony_ci 10793d0407baSopenharmony_cistatic const char *const hibernation_modes[] = { 10803d0407baSopenharmony_ci [HIBERNATION_PLATFORM] = "platform", [HIBERNATION_SHUTDOWN] = "shutdown", [HIBERNATION_REBOOT] = "reboot", 10813d0407baSopenharmony_ci#ifdef CONFIG_SUSPEND 10823d0407baSopenharmony_ci [HIBERNATION_SUSPEND] = "suspend", 10833d0407baSopenharmony_ci#endif 10843d0407baSopenharmony_ci [HIBERNATION_TEST_RESUME] = "test_resume", 10853d0407baSopenharmony_ci}; 10863d0407baSopenharmony_ci 10873d0407baSopenharmony_ci/* 10883d0407baSopenharmony_ci * /sys/power/disk - Control hibernation mode. 10893d0407baSopenharmony_ci * 10903d0407baSopenharmony_ci * Hibernation can be handled in several ways. There are a few different ways 10913d0407baSopenharmony_ci * to put the system into the sleep state: using the platform driver (e.g. ACPI 10923d0407baSopenharmony_ci * or other hibernation_ops), powering it off or rebooting it (for testing 10933d0407baSopenharmony_ci * mostly). 10943d0407baSopenharmony_ci * 10953d0407baSopenharmony_ci * The sysfs file /sys/power/disk provides an interface for selecting the 10963d0407baSopenharmony_ci * hibernation mode to use. Reading from this file causes the available modes 10973d0407baSopenharmony_ci * to be printed. There are 3 modes that can be supported: 10983d0407baSopenharmony_ci * 10993d0407baSopenharmony_ci * 'platform' 11003d0407baSopenharmony_ci * 'shutdown' 11013d0407baSopenharmony_ci * 'reboot' 11023d0407baSopenharmony_ci * 11033d0407baSopenharmony_ci * If a platform hibernation driver is in use, 'platform' will be supported 11043d0407baSopenharmony_ci * and will be used by default. Otherwise, 'shutdown' will be used by default. 11053d0407baSopenharmony_ci * The selected option (i.e. the one corresponding to the current value of 11063d0407baSopenharmony_ci * hibernation_mode) is enclosed by a square bracket. 11073d0407baSopenharmony_ci * 11083d0407baSopenharmony_ci * To select a given hibernation mode it is necessary to write the mode's 11093d0407baSopenharmony_ci * string representation (as returned by reading from /sys/power/disk) back 11103d0407baSopenharmony_ci * into /sys/power/disk. 11113d0407baSopenharmony_ci */ 11123d0407baSopenharmony_ci 11133d0407baSopenharmony_cistatic ssize_t disk_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 11143d0407baSopenharmony_ci{ 11153d0407baSopenharmony_ci int i; 11163d0407baSopenharmony_ci char *start = buf; 11173d0407baSopenharmony_ci 11183d0407baSopenharmony_ci if (!hibernation_available()) { 11193d0407baSopenharmony_ci return sprintf(buf, "[disabled]\n"); 11203d0407baSopenharmony_ci } 11213d0407baSopenharmony_ci 11223d0407baSopenharmony_ci for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) { 11233d0407baSopenharmony_ci if (!hibernation_modes[i]) { 11243d0407baSopenharmony_ci continue; 11253d0407baSopenharmony_ci } 11263d0407baSopenharmony_ci switch (i) { 11273d0407baSopenharmony_ci case HIBERNATION_SHUTDOWN: 11283d0407baSopenharmony_ci case HIBERNATION_REBOOT: 11293d0407baSopenharmony_ci#ifdef CONFIG_SUSPEND 11303d0407baSopenharmony_ci case HIBERNATION_SUSPEND: 11313d0407baSopenharmony_ci#endif 11323d0407baSopenharmony_ci case HIBERNATION_TEST_RESUME: 11333d0407baSopenharmony_ci break; 11343d0407baSopenharmony_ci case HIBERNATION_PLATFORM: 11353d0407baSopenharmony_ci if (hibernation_ops) { 11363d0407baSopenharmony_ci break; 11373d0407baSopenharmony_ci } 11383d0407baSopenharmony_ci /* not a valid mode, continue with loop */ 11393d0407baSopenharmony_ci continue; 11403d0407baSopenharmony_ci default: 11413d0407baSopenharmony_ci break; 11423d0407baSopenharmony_ci } 11433d0407baSopenharmony_ci if (i == hibernation_mode) { 11443d0407baSopenharmony_ci buf += sprintf(buf, "[%s] ", hibernation_modes[i]); 11453d0407baSopenharmony_ci } else { 11463d0407baSopenharmony_ci buf += sprintf(buf, "%s ", hibernation_modes[i]); 11473d0407baSopenharmony_ci } 11483d0407baSopenharmony_ci } 11493d0407baSopenharmony_ci buf += sprintf(buf, "\n"); 11503d0407baSopenharmony_ci return buf - start; 11513d0407baSopenharmony_ci} 11523d0407baSopenharmony_ci 11533d0407baSopenharmony_cistatic ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) 11543d0407baSopenharmony_ci{ 11553d0407baSopenharmony_ci int error = 0; 11563d0407baSopenharmony_ci int i; 11573d0407baSopenharmony_ci int len; 11583d0407baSopenharmony_ci char *p; 11593d0407baSopenharmony_ci int mode = HIBERNATION_INVALID; 11603d0407baSopenharmony_ci 11613d0407baSopenharmony_ci if (!hibernation_available()) { 11623d0407baSopenharmony_ci return -EPERM; 11633d0407baSopenharmony_ci } 11643d0407baSopenharmony_ci 11653d0407baSopenharmony_ci p = memchr(buf, '\n', n); 11663d0407baSopenharmony_ci len = p ? p - buf : n; 11673d0407baSopenharmony_ci 11683d0407baSopenharmony_ci lock_system_sleep(); 11693d0407baSopenharmony_ci for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) { 11703d0407baSopenharmony_ci if (len == strlen(hibernation_modes[i]) && !strncmp(buf, hibernation_modes[i], len)) { 11713d0407baSopenharmony_ci mode = i; 11723d0407baSopenharmony_ci break; 11733d0407baSopenharmony_ci } 11743d0407baSopenharmony_ci } 11753d0407baSopenharmony_ci if (mode != HIBERNATION_INVALID) { 11763d0407baSopenharmony_ci switch (mode) { 11773d0407baSopenharmony_ci case HIBERNATION_SHUTDOWN: 11783d0407baSopenharmony_ci case HIBERNATION_REBOOT: 11793d0407baSopenharmony_ci#ifdef CONFIG_SUSPEND 11803d0407baSopenharmony_ci case HIBERNATION_SUSPEND: 11813d0407baSopenharmony_ci#endif 11823d0407baSopenharmony_ci case HIBERNATION_TEST_RESUME: 11833d0407baSopenharmony_ci hibernation_mode = mode; 11843d0407baSopenharmony_ci break; 11853d0407baSopenharmony_ci case HIBERNATION_PLATFORM: 11863d0407baSopenharmony_ci if (hibernation_ops) { 11873d0407baSopenharmony_ci hibernation_mode = mode; 11883d0407baSopenharmony_ci } else { 11893d0407baSopenharmony_ci error = -EINVAL; 11903d0407baSopenharmony_ci } 11913d0407baSopenharmony_ci default: 11923d0407baSopenharmony_ci break; 11933d0407baSopenharmony_ci } 11943d0407baSopenharmony_ci } else { 11953d0407baSopenharmony_ci error = -EINVAL; 11963d0407baSopenharmony_ci } 11973d0407baSopenharmony_ci 11983d0407baSopenharmony_ci if (!error) { 11993d0407baSopenharmony_ci pm_pr_dbg("Hibernation mode set to '%s'\n", hibernation_modes[mode]); 12003d0407baSopenharmony_ci } 12013d0407baSopenharmony_ci unlock_system_sleep(); 12023d0407baSopenharmony_ci return error ? error : n; 12033d0407baSopenharmony_ci} 12043d0407baSopenharmony_ci 12053d0407baSopenharmony_cipower_attr(disk); 12063d0407baSopenharmony_ci 12073d0407baSopenharmony_cistatic ssize_t resume_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 12083d0407baSopenharmony_ci{ 12093d0407baSopenharmony_ci return sprintf(buf, "%d:%d\n", MAJOR(swsusp_resume_device), MINOR(swsusp_resume_device)); 12103d0407baSopenharmony_ci} 12113d0407baSopenharmony_ci 12123d0407baSopenharmony_cistatic ssize_t resume_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) 12133d0407baSopenharmony_ci{ 12143d0407baSopenharmony_ci dev_t res; 12153d0407baSopenharmony_ci int len = n; 12163d0407baSopenharmony_ci char *name; 12173d0407baSopenharmony_ci 12183d0407baSopenharmony_ci if (len && buf[len - 1] == '\n') { 12193d0407baSopenharmony_ci len--; 12203d0407baSopenharmony_ci } 12213d0407baSopenharmony_ci name = kstrndup(buf, len, GFP_KERNEL); 12223d0407baSopenharmony_ci if (!name) { 12233d0407baSopenharmony_ci return -ENOMEM; 12243d0407baSopenharmony_ci } 12253d0407baSopenharmony_ci 12263d0407baSopenharmony_ci res = name_to_dev_t(name); 12273d0407baSopenharmony_ci kfree(name); 12283d0407baSopenharmony_ci if (!res) { 12293d0407baSopenharmony_ci return -EINVAL; 12303d0407baSopenharmony_ci } 12313d0407baSopenharmony_ci 12323d0407baSopenharmony_ci lock_system_sleep(); 12333d0407baSopenharmony_ci swsusp_resume_device = res; 12343d0407baSopenharmony_ci unlock_system_sleep(); 12353d0407baSopenharmony_ci pm_pr_dbg("Configured hibernation resume from disk to %u\n", swsusp_resume_device); 12363d0407baSopenharmony_ci noresume = 0; 12373d0407baSopenharmony_ci software_resume(); 12383d0407baSopenharmony_ci return n; 12393d0407baSopenharmony_ci} 12403d0407baSopenharmony_ci 12413d0407baSopenharmony_cipower_attr(resume); 12423d0407baSopenharmony_ci 12433d0407baSopenharmony_cistatic ssize_t resume_offset_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 12443d0407baSopenharmony_ci{ 12453d0407baSopenharmony_ci return sprintf(buf, "%llu\n", (unsigned long long)swsusp_resume_block); 12463d0407baSopenharmony_ci} 12473d0407baSopenharmony_ci 12483d0407baSopenharmony_cistatic ssize_t resume_offset_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) 12493d0407baSopenharmony_ci{ 12503d0407baSopenharmony_ci unsigned long long offset; 12513d0407baSopenharmony_ci int rc; 12523d0407baSopenharmony_ci 12533d0407baSopenharmony_ci rc = kstrtoull(buf, 0, &offset); 12543d0407baSopenharmony_ci if (rc) { 12553d0407baSopenharmony_ci return rc; 12563d0407baSopenharmony_ci } 12573d0407baSopenharmony_ci swsusp_resume_block = offset; 12583d0407baSopenharmony_ci 12593d0407baSopenharmony_ci return n; 12603d0407baSopenharmony_ci} 12613d0407baSopenharmony_ci 12623d0407baSopenharmony_cipower_attr(resume_offset); 12633d0407baSopenharmony_ci 12643d0407baSopenharmony_cistatic ssize_t image_size_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 12653d0407baSopenharmony_ci{ 12663d0407baSopenharmony_ci return sprintf(buf, "%lu\n", image_size); 12673d0407baSopenharmony_ci} 12683d0407baSopenharmony_ci 12693d0407baSopenharmony_cistatic ssize_t image_size_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) 12703d0407baSopenharmony_ci{ 12713d0407baSopenharmony_ci unsigned long size; 12723d0407baSopenharmony_ci 12733d0407baSopenharmony_ci if (sscanf(buf, "%lu", &size) == 1) { 12743d0407baSopenharmony_ci image_size = size; 12753d0407baSopenharmony_ci return n; 12763d0407baSopenharmony_ci } 12773d0407baSopenharmony_ci 12783d0407baSopenharmony_ci return -EINVAL; 12793d0407baSopenharmony_ci} 12803d0407baSopenharmony_ci 12813d0407baSopenharmony_cipower_attr(image_size); 12823d0407baSopenharmony_ci 12833d0407baSopenharmony_cistatic ssize_t reserved_size_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 12843d0407baSopenharmony_ci{ 12853d0407baSopenharmony_ci return sprintf(buf, "%lu\n", reserved_size); 12863d0407baSopenharmony_ci} 12873d0407baSopenharmony_ci 12883d0407baSopenharmony_cistatic ssize_t reserved_size_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) 12893d0407baSopenharmony_ci{ 12903d0407baSopenharmony_ci unsigned long size; 12913d0407baSopenharmony_ci 12923d0407baSopenharmony_ci if (sscanf(buf, "%lu", &size) == 1) { 12933d0407baSopenharmony_ci reserved_size = size; 12943d0407baSopenharmony_ci return n; 12953d0407baSopenharmony_ci } 12963d0407baSopenharmony_ci 12973d0407baSopenharmony_ci return -EINVAL; 12983d0407baSopenharmony_ci} 12993d0407baSopenharmony_ci 13003d0407baSopenharmony_cipower_attr(reserved_size); 13013d0407baSopenharmony_ci 13023d0407baSopenharmony_cistatic struct attribute *g[] = { 13033d0407baSopenharmony_ci &disk_attr.attr, &resume_offset_attr.attr, &resume_attr.attr, &image_size_attr.attr, &reserved_size_attr.attr, NULL, 13043d0407baSopenharmony_ci}; 13053d0407baSopenharmony_ci 13063d0407baSopenharmony_cistatic const struct attribute_group attr_group = { 13073d0407baSopenharmony_ci .attrs = g, 13083d0407baSopenharmony_ci}; 13093d0407baSopenharmony_ci 13103d0407baSopenharmony_cistatic int __init pm_disk_init(void) 13113d0407baSopenharmony_ci{ 13123d0407baSopenharmony_ci return sysfs_create_group(power_kobj, &attr_group); 13133d0407baSopenharmony_ci} 13143d0407baSopenharmony_ci 13153d0407baSopenharmony_cicore_initcall(pm_disk_init); 13163d0407baSopenharmony_ci 13173d0407baSopenharmony_cistatic int __init resume_setup(char *str) 13183d0407baSopenharmony_ci{ 13193d0407baSopenharmony_ci if (noresume) { 13203d0407baSopenharmony_ci return 1; 13213d0407baSopenharmony_ci } 13223d0407baSopenharmony_ci 13233d0407baSopenharmony_ci strncpy(resume_file, str, HIBERNATE_TWOHUNDREDFIFTYFIVE); 13243d0407baSopenharmony_ci return 1; 13253d0407baSopenharmony_ci} 13263d0407baSopenharmony_ci 13273d0407baSopenharmony_cistatic int __init resume_offset_setup(char *str) 13283d0407baSopenharmony_ci{ 13293d0407baSopenharmony_ci unsigned long long offset; 13303d0407baSopenharmony_ci 13313d0407baSopenharmony_ci if (noresume) { 13323d0407baSopenharmony_ci return 1; 13333d0407baSopenharmony_ci } 13343d0407baSopenharmony_ci 13353d0407baSopenharmony_ci if (sscanf(str, "%llu", &offset) == 1) { 13363d0407baSopenharmony_ci swsusp_resume_block = offset; 13373d0407baSopenharmony_ci } 13383d0407baSopenharmony_ci 13393d0407baSopenharmony_ci return 1; 13403d0407baSopenharmony_ci} 13413d0407baSopenharmony_ci 13423d0407baSopenharmony_cistatic int __init hibernate_setup(char *str) 13433d0407baSopenharmony_ci{ 13443d0407baSopenharmony_ci if (!strncmp(str, "noresume", HIBERNATE_EIGHT)) { 13453d0407baSopenharmony_ci noresume = 1; 13463d0407baSopenharmony_ci } else if (!strncmp(str, "nocompress", HIBERNATE_TEN)) { 13473d0407baSopenharmony_ci nocompress = 1; 13483d0407baSopenharmony_ci } else if (!strncmp(str, "no", HIBERNATE_TWO)) { 13493d0407baSopenharmony_ci noresume = 1; 13503d0407baSopenharmony_ci nohibernate = 1; 13513d0407baSopenharmony_ci } else if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX) && !strncmp(str, "protect_image", HIBERNATE_THIRTEEN)) { 13523d0407baSopenharmony_ci enable_restore_image_protection(); 13533d0407baSopenharmony_ci } 13543d0407baSopenharmony_ci return 1; 13553d0407baSopenharmony_ci} 13563d0407baSopenharmony_ci 13573d0407baSopenharmony_cistatic int __init noresume_setup(char *str) 13583d0407baSopenharmony_ci{ 13593d0407baSopenharmony_ci noresume = 1; 13603d0407baSopenharmony_ci return 1; 13613d0407baSopenharmony_ci} 13623d0407baSopenharmony_ci 13633d0407baSopenharmony_cistatic int __init resumewait_setup(char *str) 13643d0407baSopenharmony_ci{ 13653d0407baSopenharmony_ci resume_wait = 1; 13663d0407baSopenharmony_ci return 1; 13673d0407baSopenharmony_ci} 13683d0407baSopenharmony_ci 13693d0407baSopenharmony_cistatic int __init resumedelay_setup(char *str) 13703d0407baSopenharmony_ci{ 13713d0407baSopenharmony_ci int rc = kstrtouint(str, 0, &resume_delay); 13723d0407baSopenharmony_ci 13733d0407baSopenharmony_ci if (rc) { 13743d0407baSopenharmony_ci pr_warn("resumedelay: bad option string '%s'\n", str); 13753d0407baSopenharmony_ci } 13763d0407baSopenharmony_ci return 1; 13773d0407baSopenharmony_ci} 13783d0407baSopenharmony_ci 13793d0407baSopenharmony_cistatic int __init nohibernate_setup(char *str) 13803d0407baSopenharmony_ci{ 13813d0407baSopenharmony_ci noresume = 1; 13823d0407baSopenharmony_ci nohibernate = 1; 13833d0407baSopenharmony_ci return 1; 13843d0407baSopenharmony_ci} 13853d0407baSopenharmony_ci 13863d0407baSopenharmony_ci__setup("noresume", noresume_setup); 13873d0407baSopenharmony_ci__setup("resume_offset=", resume_offset_setup); 13883d0407baSopenharmony_ci__setup("resume=", resume_setup); 13893d0407baSopenharmony_ci__setup("hibernate=", hibernate_setup); 13903d0407baSopenharmony_ci__setup("resumewait", resumewait_setup); 13913d0407baSopenharmony_ci__setup("resumedelay=", resumedelay_setup); 13923d0407baSopenharmony_ci__setup("nohibernate", nohibernate_setup); 1393