linux/arch/arm64/kernel/suspend.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/ftrace.h>
   3#include <linux/percpu.h>
   4#include <linux/slab.h>
   5#include <linux/uaccess.h>
   6#include <asm/alternative.h>
   7#include <asm/cacheflush.h>
   8#include <asm/cpufeature.h>
   9#include <asm/daifflags.h>
  10#include <asm/debug-monitors.h>
  11#include <asm/exec.h>
  12#include <asm/pgtable.h>
  13#include <asm/memory.h>
  14#include <asm/mmu_context.h>
  15#include <asm/smp_plat.h>
  16#include <asm/suspend.h>
  17
  18/*
  19 * This is allocated by cpu_suspend_init(), and used to store a pointer to
  20 * the 'struct sleep_stack_data' the contains a particular CPUs state.
  21 */
  22unsigned long *sleep_save_stash;
  23
  24/*
  25 * This hook is provided so that cpu_suspend code can restore HW
  26 * breakpoints as early as possible in the resume path, before reenabling
  27 * debug exceptions. Code cannot be run from a CPU PM notifier since by the
  28 * time the notifier runs debug exceptions might have been enabled already,
  29 * with HW breakpoints registers content still in an unknown state.
  30 */
  31static int (*hw_breakpoint_restore)(unsigned int);
  32void __init cpu_suspend_set_dbg_restorer(int (*hw_bp_restore)(unsigned int))
  33{
  34        /* Prevent multiple restore hook initializations */
  35        if (WARN_ON(hw_breakpoint_restore))
  36                return;
  37        hw_breakpoint_restore = hw_bp_restore;
  38}
  39
  40void notrace __cpu_suspend_exit(void)
  41{
  42        unsigned int cpu = smp_processor_id();
  43
  44        /*
  45         * We are resuming from reset with the idmap active in TTBR0_EL1.
  46         * We must uninstall the idmap and restore the expected MMU
  47         * state before we can possibly return to userspace.
  48         */
  49        cpu_uninstall_idmap();
  50
  51        /* Restore CnP bit in TTBR1_EL1 */
  52        if (system_supports_cnp())
  53                cpu_replace_ttbr1(lm_alias(swapper_pg_dir));
  54
  55        /*
  56         * PSTATE was not saved over suspend/resume, re-enable any detected
  57         * features that might not have been set correctly.
  58         */
  59        __uaccess_enable_hw_pan();
  60        uao_thread_switch(current);
  61
  62        /*
  63         * Restore HW breakpoint registers to sane values
  64         * before debug exceptions are possibly reenabled
  65         * by cpu_suspend()s local_daif_restore() call.
  66         */
  67        if (hw_breakpoint_restore)
  68                hw_breakpoint_restore(cpu);
  69
  70        /*
  71         * On resume, firmware implementing dynamic mitigation will
  72         * have turned the mitigation on. If the user has forcefully
  73         * disabled it, make sure their wishes are obeyed.
  74         */
  75        if (arm64_get_ssbd_state() == ARM64_SSBD_FORCE_DISABLE)
  76                arm64_set_ssbd_mitigation(false);
  77}
  78
  79/*
  80 * cpu_suspend
  81 *
  82 * arg: argument to pass to the finisher function
  83 * fn: finisher function pointer
  84 *
  85 */
  86int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
  87{
  88        int ret = 0;
  89        unsigned long flags;
  90        struct sleep_stack_data state;
  91
  92        /*
  93         * From this point debug exceptions are disabled to prevent
  94         * updates to mdscr register (saved and restored along with
  95         * general purpose registers) from kernel debuggers.
  96         */
  97        flags = local_daif_save();
  98
  99        /*
 100         * Function graph tracer state gets incosistent when the kernel
 101         * calls functions that never return (aka suspend finishers) hence
 102         * disable graph tracing during their execution.
 103         */
 104        pause_graph_tracing();
 105
 106        if (__cpu_suspend_enter(&state)) {
 107                /* Call the suspend finisher */
 108                ret = fn(arg);
 109
 110                /*
 111                 * Never gets here, unless the suspend finisher fails.
 112                 * Successful cpu_suspend() should return from cpu_resume(),
 113                 * returning through this code path is considered an error
 114                 * If the return value is set to 0 force ret = -EOPNOTSUPP
 115                 * to make sure a proper error condition is propagated
 116                 */
 117                if (!ret)
 118                        ret = -EOPNOTSUPP;
 119        } else {
 120                __cpu_suspend_exit();
 121        }
 122
 123        unpause_graph_tracing();
 124
 125        /*
 126         * Restore pstate flags. OS lock and mdscr have been already
 127         * restored, so from this point onwards, debugging is fully
 128         * renabled if it was enabled when core started shutdown.
 129         */
 130        local_daif_restore(flags);
 131
 132        return ret;
 133}
 134
 135static int __init cpu_suspend_init(void)
 136{
 137        /* ctx_ptr is an array of physical addresses */
 138        sleep_save_stash = kcalloc(mpidr_hash_size(), sizeof(*sleep_save_stash),
 139                                   GFP_KERNEL);
 140
 141        if (WARN_ON(!sleep_save_stash))
 142                return -ENOMEM;
 143
 144        return 0;
 145}
 146early_initcall(cpu_suspend_init);
 147