18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Hibernation support for x86-64 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2007 Rafael J. Wysocki <rjw@sisk.pl> 68c2ecf20Sopenharmony_ci * Copyright (c) 2002 Pavel Machek <pavel@ucw.cz> 78c2ecf20Sopenharmony_ci * Copyright (c) 2001 Patrick Mochel <mochel@osdl.org> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/gfp.h> 118c2ecf20Sopenharmony_ci#include <linux/smp.h> 128c2ecf20Sopenharmony_ci#include <linux/suspend.h> 138c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 148c2ecf20Sopenharmony_ci#include <linux/kdebug.h> 158c2ecf20Sopenharmony_ci#include <linux/pgtable.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <crypto/hash.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <asm/e820/api.h> 208c2ecf20Sopenharmony_ci#include <asm/init.h> 218c2ecf20Sopenharmony_ci#include <asm/proto.h> 228c2ecf20Sopenharmony_ci#include <asm/page.h> 238c2ecf20Sopenharmony_ci#include <asm/mtrr.h> 248c2ecf20Sopenharmony_ci#include <asm/sections.h> 258c2ecf20Sopenharmony_ci#include <asm/suspend.h> 268c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int set_up_temporary_text_mapping(pgd_t *pgd) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci pmd_t *pmd; 318c2ecf20Sopenharmony_ci pud_t *pud; 328c2ecf20Sopenharmony_ci p4d_t *p4d = NULL; 338c2ecf20Sopenharmony_ci pgprot_t pgtable_prot = __pgprot(_KERNPG_TABLE); 348c2ecf20Sopenharmony_ci pgprot_t pmd_text_prot = __pgprot(__PAGE_KERNEL_LARGE_EXEC); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci /* Filter out unsupported __PAGE_KERNEL* bits: */ 378c2ecf20Sopenharmony_ci pgprot_val(pmd_text_prot) &= __default_kernel_pte_mask; 388c2ecf20Sopenharmony_ci pgprot_val(pgtable_prot) &= __default_kernel_pte_mask; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci /* 418c2ecf20Sopenharmony_ci * The new mapping only has to cover the page containing the image 428c2ecf20Sopenharmony_ci * kernel's entry point (jump_address_phys), because the switch over to 438c2ecf20Sopenharmony_ci * it is carried out by relocated code running from a page allocated 448c2ecf20Sopenharmony_ci * specifically for this purpose and covered by the identity mapping, so 458c2ecf20Sopenharmony_ci * the temporary kernel text mapping is only needed for the final jump. 468c2ecf20Sopenharmony_ci * Moreover, in that mapping the virtual address of the image kernel's 478c2ecf20Sopenharmony_ci * entry point must be the same as its virtual address in the image 488c2ecf20Sopenharmony_ci * kernel (restore_jump_address), so the image kernel's 498c2ecf20Sopenharmony_ci * restore_registers() code doesn't find itself in a different area of 508c2ecf20Sopenharmony_ci * the virtual address space after switching over to the original page 518c2ecf20Sopenharmony_ci * tables used by the image kernel. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (pgtable_l5_enabled()) { 558c2ecf20Sopenharmony_ci p4d = (p4d_t *)get_safe_page(GFP_ATOMIC); 568c2ecf20Sopenharmony_ci if (!p4d) 578c2ecf20Sopenharmony_ci return -ENOMEM; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci pud = (pud_t *)get_safe_page(GFP_ATOMIC); 618c2ecf20Sopenharmony_ci if (!pud) 628c2ecf20Sopenharmony_ci return -ENOMEM; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci pmd = (pmd_t *)get_safe_page(GFP_ATOMIC); 658c2ecf20Sopenharmony_ci if (!pmd) 668c2ecf20Sopenharmony_ci return -ENOMEM; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci set_pmd(pmd + pmd_index(restore_jump_address), 698c2ecf20Sopenharmony_ci __pmd((jump_address_phys & PMD_MASK) | pgprot_val(pmd_text_prot))); 708c2ecf20Sopenharmony_ci set_pud(pud + pud_index(restore_jump_address), 718c2ecf20Sopenharmony_ci __pud(__pa(pmd) | pgprot_val(pgtable_prot))); 728c2ecf20Sopenharmony_ci if (p4d) { 738c2ecf20Sopenharmony_ci p4d_t new_p4d = __p4d(__pa(pud) | pgprot_val(pgtable_prot)); 748c2ecf20Sopenharmony_ci pgd_t new_pgd = __pgd(__pa(p4d) | pgprot_val(pgtable_prot)); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci set_p4d(p4d + p4d_index(restore_jump_address), new_p4d); 778c2ecf20Sopenharmony_ci set_pgd(pgd + pgd_index(restore_jump_address), new_pgd); 788c2ecf20Sopenharmony_ci } else { 798c2ecf20Sopenharmony_ci /* No p4d for 4-level paging: point the pgd to the pud page table */ 808c2ecf20Sopenharmony_ci pgd_t new_pgd = __pgd(__pa(pud) | pgprot_val(pgtable_prot)); 818c2ecf20Sopenharmony_ci set_pgd(pgd + pgd_index(restore_jump_address), new_pgd); 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic void *alloc_pgt_page(void *context) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci return (void *)get_safe_page(GFP_ATOMIC); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int set_up_temporary_mappings(void) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct x86_mapping_info info = { 958c2ecf20Sopenharmony_ci .alloc_pgt_page = alloc_pgt_page, 968c2ecf20Sopenharmony_ci .page_flag = __PAGE_KERNEL_LARGE_EXEC, 978c2ecf20Sopenharmony_ci .offset = __PAGE_OFFSET, 988c2ecf20Sopenharmony_ci }; 998c2ecf20Sopenharmony_ci unsigned long mstart, mend; 1008c2ecf20Sopenharmony_ci pgd_t *pgd; 1018c2ecf20Sopenharmony_ci int result; 1028c2ecf20Sopenharmony_ci int i; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci pgd = (pgd_t *)get_safe_page(GFP_ATOMIC); 1058c2ecf20Sopenharmony_ci if (!pgd) 1068c2ecf20Sopenharmony_ci return -ENOMEM; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* Prepare a temporary mapping for the kernel text */ 1098c2ecf20Sopenharmony_ci result = set_up_temporary_text_mapping(pgd); 1108c2ecf20Sopenharmony_ci if (result) 1118c2ecf20Sopenharmony_ci return result; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* Set up the direct mapping from scratch */ 1148c2ecf20Sopenharmony_ci for (i = 0; i < nr_pfn_mapped; i++) { 1158c2ecf20Sopenharmony_ci mstart = pfn_mapped[i].start << PAGE_SHIFT; 1168c2ecf20Sopenharmony_ci mend = pfn_mapped[i].end << PAGE_SHIFT; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci result = kernel_ident_mapping_init(&info, pgd, mstart, mend); 1198c2ecf20Sopenharmony_ci if (result) 1208c2ecf20Sopenharmony_ci return result; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci temp_pgt = __pa(pgd); 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ciasmlinkage int swsusp_arch_resume(void) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci int error; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* We have got enough memory and from now on we cannot recover */ 1328c2ecf20Sopenharmony_ci error = set_up_temporary_mappings(); 1338c2ecf20Sopenharmony_ci if (error) 1348c2ecf20Sopenharmony_ci return error; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci error = relocate_restore_code(); 1378c2ecf20Sopenharmony_ci if (error) 1388c2ecf20Sopenharmony_ci return error; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci restore_image(); 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci} 143