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