1 // SPDX-License-Identifier: GPL-2.0-only
3 * Hibernate support specific for RISCV
5 * Copyright (C) 2022 Shanghai StarFive Technology Co., Ltd.
7 * Author: Jee Heng Sia <jeeheng.sia@starfivetech.com>
11 #include <linux/cpu.h>
13 #include <linux/sched.h>
14 #include <linux/suspend.h>
15 #include <linux/utsname.h>
17 #include <asm/barrier.h>
18 #include <asm/cacheflush.h>
19 #include <asm/irqflags.h>
20 #include <asm/kexec.h>
21 #include <asm/mmu_context.h>
23 #include <asm/sections.h>
25 #include <asm/suspend.h>
28 * The logical cpu number we should resume on, initialised to a non-cpu
31 static int sleep_cpu = -EINVAL;
34 * Values that may not change over hibernate/resume. We put the build number
35 * and date in here so that we guarantee not to resume with a different
38 struct arch_hibernate_hdr_invariants {
39 char uts_version[__NEW_UTS_LEN + 1];
42 /* These values need to be known across a hibernate/restore. */
43 static struct arch_hibernate_hdr {
44 struct arch_hibernate_hdr_invariants invariants;
48 static inline void arch_hdr_invariants(struct arch_hibernate_hdr_invariants *i)
50 memset(i, 0, sizeof(*i));
51 memcpy(i->uts_version, init_utsname()->version, sizeof(i->uts_version));
55 int pfn_is_nosave(unsigned long pfn)
57 unsigned long nosave_begin_pfn = sym_to_pfn(&__nosave_begin);
58 unsigned long nosave_end_pfn = sym_to_pfn(&__nosave_end - 1);
60 return ((pfn >= nosave_begin_pfn) && (pfn <= nosave_end_pfn));
63 void notrace save_processor_state(void)
65 WARN_ON(num_online_cpus() != 1);
68 void notrace restore_processor_state(void)
73 int arch_hibernation_header_save(void *addr, unsigned int max_size)
75 struct arch_hibernate_hdr *hdr = addr;
77 if (max_size < sizeof(*hdr))
80 arch_hdr_invariants(&hdr->invariants);
82 hdr->hartid = cpuid_to_hartid_map(sleep_cpu);
84 pr_debug("Hibernating on CPU %x hartid %llx\n", sleep_cpu, hdr->hartid);
88 EXPORT_SYMBOL(arch_hibernation_header_save);
90 int arch_hibernation_header_restore(void *addr)
92 struct arch_hibernate_hdr_invariants invariants;
93 struct arch_hibernate_hdr *hdr = addr;
96 arch_hdr_invariants(&invariants);
98 if (memcmp(&hdr->invariants, &invariants, sizeof(invariants))) {
99 pr_crit("Hibernate image not generated by this kernel!\n");
103 sleep_cpu = riscv_hartid_to_cpuid(hdr->hartid);
105 pr_crit("Hibernated on a CPU not known to this kernel!\n");
110 pr_debug("Hibernated on CPU %x hartid %llx\n", sleep_cpu, hdr->hartid);
112 ret = bringup_hibernate_cpu(sleep_cpu);
122 EXPORT_SYMBOL(arch_hibernation_header_restore);
124 int swsusp_arch_suspend(void)
129 int swsusp_arch_resume(void)
134 int hibernate_resume_nonboot_cpu_disable(void)
137 pr_err("Failing to resume from hibernate on an unknown CPU.\n");
141 return freeze_secondary_cpus(sleep_cpu);