1*c0317210SSia Jee Heng // SPDX-License-Identifier: GPL-2.0-only 2*c0317210SSia Jee Heng /* 3*c0317210SSia Jee Heng * Hibernation support for RISCV 4*c0317210SSia Jee Heng * 5*c0317210SSia Jee Heng * Copyright (C) 2023 StarFive Technology Co., Ltd. 6*c0317210SSia Jee Heng * 7*c0317210SSia Jee Heng * Author: Jee Heng Sia <jeeheng.sia@starfivetech.com> 8*c0317210SSia Jee Heng */ 9*c0317210SSia Jee Heng 10*c0317210SSia Jee Heng #include <asm/barrier.h> 11*c0317210SSia Jee Heng #include <asm/cacheflush.h> 12*c0317210SSia Jee Heng #include <asm/mmu_context.h> 13*c0317210SSia Jee Heng #include <asm/page.h> 14*c0317210SSia Jee Heng #include <asm/pgalloc.h> 15*c0317210SSia Jee Heng #include <asm/pgtable.h> 16*c0317210SSia Jee Heng #include <asm/sections.h> 17*c0317210SSia Jee Heng #include <asm/set_memory.h> 18*c0317210SSia Jee Heng #include <asm/smp.h> 19*c0317210SSia Jee Heng #include <asm/suspend.h> 20*c0317210SSia Jee Heng 21*c0317210SSia Jee Heng #include <linux/cpu.h> 22*c0317210SSia Jee Heng #include <linux/memblock.h> 23*c0317210SSia Jee Heng #include <linux/pm.h> 24*c0317210SSia Jee Heng #include <linux/sched.h> 25*c0317210SSia Jee Heng #include <linux/suspend.h> 26*c0317210SSia Jee Heng #include <linux/utsname.h> 27*c0317210SSia Jee Heng 28*c0317210SSia Jee Heng /* The logical cpu number we should resume on, initialised to a non-cpu number. */ 29*c0317210SSia Jee Heng static int sleep_cpu = -EINVAL; 30*c0317210SSia Jee Heng 31*c0317210SSia Jee Heng /* Pointer to the temporary resume page table. */ 32*c0317210SSia Jee Heng static pgd_t *resume_pg_dir; 33*c0317210SSia Jee Heng 34*c0317210SSia Jee Heng /* CPU context to be saved. */ 35*c0317210SSia Jee Heng struct suspend_context *hibernate_cpu_context; 36*c0317210SSia Jee Heng EXPORT_SYMBOL_GPL(hibernate_cpu_context); 37*c0317210SSia Jee Heng 38*c0317210SSia Jee Heng unsigned long relocated_restore_code; 39*c0317210SSia Jee Heng EXPORT_SYMBOL_GPL(relocated_restore_code); 40*c0317210SSia Jee Heng 41*c0317210SSia Jee Heng /** 42*c0317210SSia Jee Heng * struct arch_hibernate_hdr_invariants - container to store kernel build version. 43*c0317210SSia Jee Heng * @uts_version: to save the build number and date so that we do not resume with 44*c0317210SSia Jee Heng * a different kernel. 45*c0317210SSia Jee Heng */ 46*c0317210SSia Jee Heng struct arch_hibernate_hdr_invariants { 47*c0317210SSia Jee Heng char uts_version[__NEW_UTS_LEN + 1]; 48*c0317210SSia Jee Heng }; 49*c0317210SSia Jee Heng 50*c0317210SSia Jee Heng /** 51*c0317210SSia Jee Heng * struct arch_hibernate_hdr - helper parameters that help us to restore the image. 52*c0317210SSia Jee Heng * @invariants: container to store kernel build version. 53*c0317210SSia Jee Heng * @hartid: to make sure same boot_cpu executes the hibernate/restore code. 54*c0317210SSia Jee Heng * @saved_satp: original page table used by the hibernated image. 55*c0317210SSia Jee Heng * @restore_cpu_addr: the kernel's image address to restore the CPU context. 56*c0317210SSia Jee Heng */ 57*c0317210SSia Jee Heng static struct arch_hibernate_hdr { 58*c0317210SSia Jee Heng struct arch_hibernate_hdr_invariants invariants; 59*c0317210SSia Jee Heng unsigned long hartid; 60*c0317210SSia Jee Heng unsigned long saved_satp; 61*c0317210SSia Jee Heng unsigned long restore_cpu_addr; 62*c0317210SSia Jee Heng } resume_hdr; 63*c0317210SSia Jee Heng 64*c0317210SSia Jee Heng static void arch_hdr_invariants(struct arch_hibernate_hdr_invariants *i) 65*c0317210SSia Jee Heng { 66*c0317210SSia Jee Heng memset(i, 0, sizeof(*i)); 67*c0317210SSia Jee Heng memcpy(i->uts_version, init_utsname()->version, sizeof(i->uts_version)); 68*c0317210SSia Jee Heng } 69*c0317210SSia Jee Heng 70*c0317210SSia Jee Heng /* 71*c0317210SSia Jee Heng * Check if the given pfn is in the 'nosave' section. 72*c0317210SSia Jee Heng */ 73*c0317210SSia Jee Heng int pfn_is_nosave(unsigned long pfn) 74*c0317210SSia Jee Heng { 75*c0317210SSia Jee Heng unsigned long nosave_begin_pfn = sym_to_pfn(&__nosave_begin); 76*c0317210SSia Jee Heng unsigned long nosave_end_pfn = sym_to_pfn(&__nosave_end - 1); 77*c0317210SSia Jee Heng 78*c0317210SSia Jee Heng return ((pfn >= nosave_begin_pfn) && (pfn <= nosave_end_pfn)); 79*c0317210SSia Jee Heng } 80*c0317210SSia Jee Heng 81*c0317210SSia Jee Heng void notrace save_processor_state(void) 82*c0317210SSia Jee Heng { 83*c0317210SSia Jee Heng WARN_ON(num_online_cpus() != 1); 84*c0317210SSia Jee Heng } 85*c0317210SSia Jee Heng 86*c0317210SSia Jee Heng void notrace restore_processor_state(void) 87*c0317210SSia Jee Heng { 88*c0317210SSia Jee Heng } 89*c0317210SSia Jee Heng 90*c0317210SSia Jee Heng /* 91*c0317210SSia Jee Heng * Helper parameters need to be saved to the hibernation image header. 92*c0317210SSia Jee Heng */ 93*c0317210SSia Jee Heng int arch_hibernation_header_save(void *addr, unsigned int max_size) 94*c0317210SSia Jee Heng { 95*c0317210SSia Jee Heng struct arch_hibernate_hdr *hdr = addr; 96*c0317210SSia Jee Heng 97*c0317210SSia Jee Heng if (max_size < sizeof(*hdr)) 98*c0317210SSia Jee Heng return -EOVERFLOW; 99*c0317210SSia Jee Heng 100*c0317210SSia Jee Heng arch_hdr_invariants(&hdr->invariants); 101*c0317210SSia Jee Heng 102*c0317210SSia Jee Heng hdr->hartid = cpuid_to_hartid_map(sleep_cpu); 103*c0317210SSia Jee Heng hdr->saved_satp = csr_read(CSR_SATP); 104*c0317210SSia Jee Heng hdr->restore_cpu_addr = (unsigned long)__hibernate_cpu_resume; 105*c0317210SSia Jee Heng 106*c0317210SSia Jee Heng return 0; 107*c0317210SSia Jee Heng } 108*c0317210SSia Jee Heng EXPORT_SYMBOL_GPL(arch_hibernation_header_save); 109*c0317210SSia Jee Heng 110*c0317210SSia Jee Heng /* 111*c0317210SSia Jee Heng * Retrieve the helper parameters from the hibernation image header. 112*c0317210SSia Jee Heng */ 113*c0317210SSia Jee Heng int arch_hibernation_header_restore(void *addr) 114*c0317210SSia Jee Heng { 115*c0317210SSia Jee Heng struct arch_hibernate_hdr_invariants invariants; 116*c0317210SSia Jee Heng struct arch_hibernate_hdr *hdr = addr; 117*c0317210SSia Jee Heng int ret = 0; 118*c0317210SSia Jee Heng 119*c0317210SSia Jee Heng arch_hdr_invariants(&invariants); 120*c0317210SSia Jee Heng 121*c0317210SSia Jee Heng if (memcmp(&hdr->invariants, &invariants, sizeof(invariants))) { 122*c0317210SSia Jee Heng pr_crit("Hibernate image not generated by this kernel!\n"); 123*c0317210SSia Jee Heng return -EINVAL; 124*c0317210SSia Jee Heng } 125*c0317210SSia Jee Heng 126*c0317210SSia Jee Heng sleep_cpu = riscv_hartid_to_cpuid(hdr->hartid); 127*c0317210SSia Jee Heng if (sleep_cpu < 0) { 128*c0317210SSia Jee Heng pr_crit("Hibernated on a CPU not known to this kernel!\n"); 129*c0317210SSia Jee Heng sleep_cpu = -EINVAL; 130*c0317210SSia Jee Heng return -EINVAL; 131*c0317210SSia Jee Heng } 132*c0317210SSia Jee Heng 133*c0317210SSia Jee Heng #ifdef CONFIG_SMP 134*c0317210SSia Jee Heng ret = bringup_hibernate_cpu(sleep_cpu); 135*c0317210SSia Jee Heng if (ret) { 136*c0317210SSia Jee Heng sleep_cpu = -EINVAL; 137*c0317210SSia Jee Heng return ret; 138*c0317210SSia Jee Heng } 139*c0317210SSia Jee Heng #endif 140*c0317210SSia Jee Heng resume_hdr = *hdr; 141*c0317210SSia Jee Heng 142*c0317210SSia Jee Heng return ret; 143*c0317210SSia Jee Heng } 144*c0317210SSia Jee Heng EXPORT_SYMBOL_GPL(arch_hibernation_header_restore); 145*c0317210SSia Jee Heng 146*c0317210SSia Jee Heng int swsusp_arch_suspend(void) 147*c0317210SSia Jee Heng { 148*c0317210SSia Jee Heng int ret = 0; 149*c0317210SSia Jee Heng 150*c0317210SSia Jee Heng if (__cpu_suspend_enter(hibernate_cpu_context)) { 151*c0317210SSia Jee Heng sleep_cpu = smp_processor_id(); 152*c0317210SSia Jee Heng suspend_save_csrs(hibernate_cpu_context); 153*c0317210SSia Jee Heng ret = swsusp_save(); 154*c0317210SSia Jee Heng } else { 155*c0317210SSia Jee Heng suspend_restore_csrs(hibernate_cpu_context); 156*c0317210SSia Jee Heng flush_tlb_all(); 157*c0317210SSia Jee Heng flush_icache_all(); 158*c0317210SSia Jee Heng 159*c0317210SSia Jee Heng /* 160*c0317210SSia Jee Heng * Tell the hibernation core that we've just restored the memory. 161*c0317210SSia Jee Heng */ 162*c0317210SSia Jee Heng in_suspend = 0; 163*c0317210SSia Jee Heng sleep_cpu = -EINVAL; 164*c0317210SSia Jee Heng } 165*c0317210SSia Jee Heng 166*c0317210SSia Jee Heng return ret; 167*c0317210SSia Jee Heng } 168*c0317210SSia Jee Heng 169*c0317210SSia Jee Heng static int temp_pgtable_map_pte(pmd_t *dst_pmdp, pmd_t *src_pmdp, unsigned long start, 170*c0317210SSia Jee Heng unsigned long end, pgprot_t prot) 171*c0317210SSia Jee Heng { 172*c0317210SSia Jee Heng pte_t *src_ptep; 173*c0317210SSia Jee Heng pte_t *dst_ptep; 174*c0317210SSia Jee Heng 175*c0317210SSia Jee Heng if (pmd_none(READ_ONCE(*dst_pmdp))) { 176*c0317210SSia Jee Heng dst_ptep = (pte_t *)get_safe_page(GFP_ATOMIC); 177*c0317210SSia Jee Heng if (!dst_ptep) 178*c0317210SSia Jee Heng return -ENOMEM; 179*c0317210SSia Jee Heng 180*c0317210SSia Jee Heng pmd_populate_kernel(NULL, dst_pmdp, dst_ptep); 181*c0317210SSia Jee Heng } 182*c0317210SSia Jee Heng 183*c0317210SSia Jee Heng dst_ptep = pte_offset_kernel(dst_pmdp, start); 184*c0317210SSia Jee Heng src_ptep = pte_offset_kernel(src_pmdp, start); 185*c0317210SSia Jee Heng 186*c0317210SSia Jee Heng do { 187*c0317210SSia Jee Heng pte_t pte = READ_ONCE(*src_ptep); 188*c0317210SSia Jee Heng 189*c0317210SSia Jee Heng if (pte_present(pte)) 190*c0317210SSia Jee Heng set_pte(dst_ptep, __pte(pte_val(pte) | pgprot_val(prot))); 191*c0317210SSia Jee Heng } while (dst_ptep++, src_ptep++, start += PAGE_SIZE, start < end); 192*c0317210SSia Jee Heng 193*c0317210SSia Jee Heng return 0; 194*c0317210SSia Jee Heng } 195*c0317210SSia Jee Heng 196*c0317210SSia Jee Heng static int temp_pgtable_map_pmd(pud_t *dst_pudp, pud_t *src_pudp, unsigned long start, 197*c0317210SSia Jee Heng unsigned long end, pgprot_t prot) 198*c0317210SSia Jee Heng { 199*c0317210SSia Jee Heng unsigned long next; 200*c0317210SSia Jee Heng unsigned long ret; 201*c0317210SSia Jee Heng pmd_t *src_pmdp; 202*c0317210SSia Jee Heng pmd_t *dst_pmdp; 203*c0317210SSia Jee Heng 204*c0317210SSia Jee Heng if (pud_none(READ_ONCE(*dst_pudp))) { 205*c0317210SSia Jee Heng dst_pmdp = (pmd_t *)get_safe_page(GFP_ATOMIC); 206*c0317210SSia Jee Heng if (!dst_pmdp) 207*c0317210SSia Jee Heng return -ENOMEM; 208*c0317210SSia Jee Heng 209*c0317210SSia Jee Heng pud_populate(NULL, dst_pudp, dst_pmdp); 210*c0317210SSia Jee Heng } 211*c0317210SSia Jee Heng 212*c0317210SSia Jee Heng dst_pmdp = pmd_offset(dst_pudp, start); 213*c0317210SSia Jee Heng src_pmdp = pmd_offset(src_pudp, start); 214*c0317210SSia Jee Heng 215*c0317210SSia Jee Heng do { 216*c0317210SSia Jee Heng pmd_t pmd = READ_ONCE(*src_pmdp); 217*c0317210SSia Jee Heng 218*c0317210SSia Jee Heng next = pmd_addr_end(start, end); 219*c0317210SSia Jee Heng 220*c0317210SSia Jee Heng if (pmd_none(pmd)) 221*c0317210SSia Jee Heng continue; 222*c0317210SSia Jee Heng 223*c0317210SSia Jee Heng if (pmd_leaf(pmd)) { 224*c0317210SSia Jee Heng set_pmd(dst_pmdp, __pmd(pmd_val(pmd) | pgprot_val(prot))); 225*c0317210SSia Jee Heng } else { 226*c0317210SSia Jee Heng ret = temp_pgtable_map_pte(dst_pmdp, src_pmdp, start, next, prot); 227*c0317210SSia Jee Heng if (ret) 228*c0317210SSia Jee Heng return -ENOMEM; 229*c0317210SSia Jee Heng } 230*c0317210SSia Jee Heng } while (dst_pmdp++, src_pmdp++, start = next, start != end); 231*c0317210SSia Jee Heng 232*c0317210SSia Jee Heng return 0; 233*c0317210SSia Jee Heng } 234*c0317210SSia Jee Heng 235*c0317210SSia Jee Heng static int temp_pgtable_map_pud(p4d_t *dst_p4dp, p4d_t *src_p4dp, unsigned long start, 236*c0317210SSia Jee Heng unsigned long end, pgprot_t prot) 237*c0317210SSia Jee Heng { 238*c0317210SSia Jee Heng unsigned long next; 239*c0317210SSia Jee Heng unsigned long ret; 240*c0317210SSia Jee Heng pud_t *dst_pudp; 241*c0317210SSia Jee Heng pud_t *src_pudp; 242*c0317210SSia Jee Heng 243*c0317210SSia Jee Heng if (p4d_none(READ_ONCE(*dst_p4dp))) { 244*c0317210SSia Jee Heng dst_pudp = (pud_t *)get_safe_page(GFP_ATOMIC); 245*c0317210SSia Jee Heng if (!dst_pudp) 246*c0317210SSia Jee Heng return -ENOMEM; 247*c0317210SSia Jee Heng 248*c0317210SSia Jee Heng p4d_populate(NULL, dst_p4dp, dst_pudp); 249*c0317210SSia Jee Heng } 250*c0317210SSia Jee Heng 251*c0317210SSia Jee Heng dst_pudp = pud_offset(dst_p4dp, start); 252*c0317210SSia Jee Heng src_pudp = pud_offset(src_p4dp, start); 253*c0317210SSia Jee Heng 254*c0317210SSia Jee Heng do { 255*c0317210SSia Jee Heng pud_t pud = READ_ONCE(*src_pudp); 256*c0317210SSia Jee Heng 257*c0317210SSia Jee Heng next = pud_addr_end(start, end); 258*c0317210SSia Jee Heng 259*c0317210SSia Jee Heng if (pud_none(pud)) 260*c0317210SSia Jee Heng continue; 261*c0317210SSia Jee Heng 262*c0317210SSia Jee Heng if (pud_leaf(pud)) { 263*c0317210SSia Jee Heng set_pud(dst_pudp, __pud(pud_val(pud) | pgprot_val(prot))); 264*c0317210SSia Jee Heng } else { 265*c0317210SSia Jee Heng ret = temp_pgtable_map_pmd(dst_pudp, src_pudp, start, next, prot); 266*c0317210SSia Jee Heng if (ret) 267*c0317210SSia Jee Heng return -ENOMEM; 268*c0317210SSia Jee Heng } 269*c0317210SSia Jee Heng } while (dst_pudp++, src_pudp++, start = next, start != end); 270*c0317210SSia Jee Heng 271*c0317210SSia Jee Heng return 0; 272*c0317210SSia Jee Heng } 273*c0317210SSia Jee Heng 274*c0317210SSia Jee Heng static int temp_pgtable_map_p4d(pgd_t *dst_pgdp, pgd_t *src_pgdp, unsigned long start, 275*c0317210SSia Jee Heng unsigned long end, pgprot_t prot) 276*c0317210SSia Jee Heng { 277*c0317210SSia Jee Heng unsigned long next; 278*c0317210SSia Jee Heng unsigned long ret; 279*c0317210SSia Jee Heng p4d_t *dst_p4dp; 280*c0317210SSia Jee Heng p4d_t *src_p4dp; 281*c0317210SSia Jee Heng 282*c0317210SSia Jee Heng if (pgd_none(READ_ONCE(*dst_pgdp))) { 283*c0317210SSia Jee Heng dst_p4dp = (p4d_t *)get_safe_page(GFP_ATOMIC); 284*c0317210SSia Jee Heng if (!dst_p4dp) 285*c0317210SSia Jee Heng return -ENOMEM; 286*c0317210SSia Jee Heng 287*c0317210SSia Jee Heng pgd_populate(NULL, dst_pgdp, dst_p4dp); 288*c0317210SSia Jee Heng } 289*c0317210SSia Jee Heng 290*c0317210SSia Jee Heng dst_p4dp = p4d_offset(dst_pgdp, start); 291*c0317210SSia Jee Heng src_p4dp = p4d_offset(src_pgdp, start); 292*c0317210SSia Jee Heng 293*c0317210SSia Jee Heng do { 294*c0317210SSia Jee Heng p4d_t p4d = READ_ONCE(*src_p4dp); 295*c0317210SSia Jee Heng 296*c0317210SSia Jee Heng next = p4d_addr_end(start, end); 297*c0317210SSia Jee Heng 298*c0317210SSia Jee Heng if (p4d_none(p4d)) 299*c0317210SSia Jee Heng continue; 300*c0317210SSia Jee Heng 301*c0317210SSia Jee Heng if (p4d_leaf(p4d)) { 302*c0317210SSia Jee Heng set_p4d(dst_p4dp, __p4d(p4d_val(p4d) | pgprot_val(prot))); 303*c0317210SSia Jee Heng } else { 304*c0317210SSia Jee Heng ret = temp_pgtable_map_pud(dst_p4dp, src_p4dp, start, next, prot); 305*c0317210SSia Jee Heng if (ret) 306*c0317210SSia Jee Heng return -ENOMEM; 307*c0317210SSia Jee Heng } 308*c0317210SSia Jee Heng } while (dst_p4dp++, src_p4dp++, start = next, start != end); 309*c0317210SSia Jee Heng 310*c0317210SSia Jee Heng return 0; 311*c0317210SSia Jee Heng } 312*c0317210SSia Jee Heng 313*c0317210SSia Jee Heng static int temp_pgtable_mapping(pgd_t *pgdp, unsigned long start, unsigned long end, pgprot_t prot) 314*c0317210SSia Jee Heng { 315*c0317210SSia Jee Heng pgd_t *dst_pgdp = pgd_offset_pgd(pgdp, start); 316*c0317210SSia Jee Heng pgd_t *src_pgdp = pgd_offset_k(start); 317*c0317210SSia Jee Heng unsigned long next; 318*c0317210SSia Jee Heng unsigned long ret; 319*c0317210SSia Jee Heng 320*c0317210SSia Jee Heng do { 321*c0317210SSia Jee Heng pgd_t pgd = READ_ONCE(*src_pgdp); 322*c0317210SSia Jee Heng 323*c0317210SSia Jee Heng next = pgd_addr_end(start, end); 324*c0317210SSia Jee Heng 325*c0317210SSia Jee Heng if (pgd_none(pgd)) 326*c0317210SSia Jee Heng continue; 327*c0317210SSia Jee Heng 328*c0317210SSia Jee Heng if (pgd_leaf(pgd)) { 329*c0317210SSia Jee Heng set_pgd(dst_pgdp, __pgd(pgd_val(pgd) | pgprot_val(prot))); 330*c0317210SSia Jee Heng } else { 331*c0317210SSia Jee Heng ret = temp_pgtable_map_p4d(dst_pgdp, src_pgdp, start, next, prot); 332*c0317210SSia Jee Heng if (ret) 333*c0317210SSia Jee Heng return -ENOMEM; 334*c0317210SSia Jee Heng } 335*c0317210SSia Jee Heng } while (dst_pgdp++, src_pgdp++, start = next, start != end); 336*c0317210SSia Jee Heng 337*c0317210SSia Jee Heng return 0; 338*c0317210SSia Jee Heng } 339*c0317210SSia Jee Heng 340*c0317210SSia Jee Heng static unsigned long relocate_restore_code(void) 341*c0317210SSia Jee Heng { 342*c0317210SSia Jee Heng void *page = (void *)get_safe_page(GFP_ATOMIC); 343*c0317210SSia Jee Heng 344*c0317210SSia Jee Heng if (!page) 345*c0317210SSia Jee Heng return -ENOMEM; 346*c0317210SSia Jee Heng 347*c0317210SSia Jee Heng copy_page(page, hibernate_core_restore_code); 348*c0317210SSia Jee Heng 349*c0317210SSia Jee Heng /* Make the page containing the relocated code executable. */ 350*c0317210SSia Jee Heng set_memory_x((unsigned long)page, 1); 351*c0317210SSia Jee Heng 352*c0317210SSia Jee Heng return (unsigned long)page; 353*c0317210SSia Jee Heng } 354*c0317210SSia Jee Heng 355*c0317210SSia Jee Heng int swsusp_arch_resume(void) 356*c0317210SSia Jee Heng { 357*c0317210SSia Jee Heng unsigned long end = (unsigned long)pfn_to_virt(max_low_pfn); 358*c0317210SSia Jee Heng unsigned long start = PAGE_OFFSET; 359*c0317210SSia Jee Heng int ret; 360*c0317210SSia Jee Heng 361*c0317210SSia Jee Heng /* 362*c0317210SSia Jee Heng * Memory allocated by get_safe_page() will be dealt with by the hibernation core, 363*c0317210SSia Jee Heng * we don't need to free it here. 364*c0317210SSia Jee Heng */ 365*c0317210SSia Jee Heng resume_pg_dir = (pgd_t *)get_safe_page(GFP_ATOMIC); 366*c0317210SSia Jee Heng if (!resume_pg_dir) 367*c0317210SSia Jee Heng return -ENOMEM; 368*c0317210SSia Jee Heng 369*c0317210SSia Jee Heng /* 370*c0317210SSia Jee Heng * Create a temporary page table and map the whole linear region as executable and 371*c0317210SSia Jee Heng * writable. 372*c0317210SSia Jee Heng */ 373*c0317210SSia Jee Heng ret = temp_pgtable_mapping(resume_pg_dir, start, end, __pgprot(_PAGE_WRITE | _PAGE_EXEC)); 374*c0317210SSia Jee Heng if (ret) 375*c0317210SSia Jee Heng return ret; 376*c0317210SSia Jee Heng 377*c0317210SSia Jee Heng /* Move the restore code to a new page so that it doesn't get overwritten by itself. */ 378*c0317210SSia Jee Heng relocated_restore_code = relocate_restore_code(); 379*c0317210SSia Jee Heng if (relocated_restore_code == -ENOMEM) 380*c0317210SSia Jee Heng return -ENOMEM; 381*c0317210SSia Jee Heng 382*c0317210SSia Jee Heng /* 383*c0317210SSia Jee Heng * Map the __hibernate_cpu_resume() address to the temporary page table so that the 384*c0317210SSia Jee Heng * restore code can jumps to it after finished restore the image. The next execution 385*c0317210SSia Jee Heng * code doesn't find itself in a different address space after switching over to the 386*c0317210SSia Jee Heng * original page table used by the hibernated image. 387*c0317210SSia Jee Heng * The __hibernate_cpu_resume() mapping is unnecessary for RV32 since the kernel and 388*c0317210SSia Jee Heng * linear addresses are identical, but different for RV64. To ensure consistency, we 389*c0317210SSia Jee Heng * map it for both RV32 and RV64 kernels. 390*c0317210SSia Jee Heng * Additionally, we should ensure that the page is writable before restoring the image. 391*c0317210SSia Jee Heng */ 392*c0317210SSia Jee Heng start = (unsigned long)resume_hdr.restore_cpu_addr; 393*c0317210SSia Jee Heng end = start + PAGE_SIZE; 394*c0317210SSia Jee Heng 395*c0317210SSia Jee Heng ret = temp_pgtable_mapping(resume_pg_dir, start, end, __pgprot(_PAGE_WRITE)); 396*c0317210SSia Jee Heng if (ret) 397*c0317210SSia Jee Heng return ret; 398*c0317210SSia Jee Heng 399*c0317210SSia Jee Heng hibernate_restore_image(resume_hdr.saved_satp, (PFN_DOWN(__pa(resume_pg_dir)) | satp_mode), 400*c0317210SSia Jee Heng resume_hdr.restore_cpu_addr); 401*c0317210SSia Jee Heng 402*c0317210SSia Jee Heng return 0; 403*c0317210SSia Jee Heng } 404*c0317210SSia Jee Heng 405*c0317210SSia Jee Heng #ifdef CONFIG_PM_SLEEP_SMP 406*c0317210SSia Jee Heng int hibernate_resume_nonboot_cpu_disable(void) 407*c0317210SSia Jee Heng { 408*c0317210SSia Jee Heng if (sleep_cpu < 0) { 409*c0317210SSia Jee Heng pr_err("Failing to resume from hibernate on an unknown CPU\n"); 410*c0317210SSia Jee Heng return -ENODEV; 411*c0317210SSia Jee Heng } 412*c0317210SSia Jee Heng 413*c0317210SSia Jee Heng return freeze_secondary_cpus(sleep_cpu); 414*c0317210SSia Jee Heng } 415*c0317210SSia Jee Heng #endif 416*c0317210SSia Jee Heng 417*c0317210SSia Jee Heng static int __init riscv_hibernate_init(void) 418*c0317210SSia Jee Heng { 419*c0317210SSia Jee Heng hibernate_cpu_context = kzalloc(sizeof(*hibernate_cpu_context), GFP_KERNEL); 420*c0317210SSia Jee Heng 421*c0317210SSia Jee Heng if (WARN_ON(!hibernate_cpu_context)) 422*c0317210SSia Jee Heng return -ENOMEM; 423*c0317210SSia Jee Heng 424*c0317210SSia Jee Heng return 0; 425*c0317210SSia Jee Heng } 426*c0317210SSia Jee Heng 427*c0317210SSia Jee Heng early_initcall(riscv_hibernate_init); 428