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        /*
  52         * PSTATE was not saved over suspend/resume, re-enable any detected
  53         * features that might not have been set correctly.
  54         */
  55        __uaccess_enable_hw_pan();
  56        uao_thread_switch(current);
  57
  58        /*
  59         * Restore HW breakpoint registers to sane values
  60         * before debug exceptions are possibly reenabled
  61         * by cpu_suspend()s local_daif_restore() call.
  62         */
  63        if (hw_breakpoint_restore)
  64                hw_breakpoint_restore(cpu);
  65}
  66
  67/*
  68 * cpu_suspend
  69 *
  70 * arg: argument to pass to the finisher function
  71 * fn: finisher function pointer
  72 *
  73 */
  74int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
  75{
  76        int ret = 0;
  77        unsigned long flags;
  78        struct sleep_stack_data state;
  79
  80        /*
  81         * From this point debug exceptions are disabled to prevent
  82         * updates to mdscr register (saved and restored along with
  83         * general purpose registers) from kernel debuggers.
  84         */
  85        flags = local_daif_save();
  86
  87        /*
  88         * Function graph tracer state gets incosistent when the kernel
  89         * calls functions that never return (aka suspend finishers) hence
  90         * disable graph tracing during their execution.
  91         */
  92        pause_graph_tracing();
  93
  94        if (__cpu_suspend_enter(&state)) {
  95                /* Call the suspend finisher */
  96                ret = fn(arg);
  97
  98                /*
  99                 * Never gets here, unless the suspend finisher fails.
 100                 * Successful cpu_suspend() should return from cpu_resume(),
 101                 * returning through this code path is considered an error
 102                 * If the return value is set to 0 force ret = -EOPNOTSUPP
 103                 * to make sure a proper error condition is propagated
 104                 */
 105                if (!ret)
 106                        ret = -EOPNOTSUPP;
 107        } else {
 108                __cpu_suspend_exit();
 109        }
 110
 111        unpause_graph_tracing();
 112
 113        /*
 114         * Restore pstate flags. OS lock and mdscr have been already
 115         * restored, so from this point onwards, debugging is fully
 116         * renabled if it was enabled when core started shutdown.
 117         */
 118        local_daif_restore(flags);
 119
 120        return ret;
 121}
 122
 123static int __init cpu_suspend_init(void)
 124{
 125        /* ctx_ptr is an array of physical addresses */
 126        sleep_save_stash = kcalloc(mpidr_hash_size(), sizeof(*sleep_save_stash),
 127                                   GFP_KERNEL);
 128
 129        if (WARN_ON(!sleep_save_stash))
 130                return -ENOMEM;
 131
 132        return 0;
 133}
 134early_initcall(cpu_suspend_init);
 135