162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Hibernation support for x86 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2007 Rafael J. Wysocki <rjw@sisk.pl> 662306a36Sopenharmony_ci * Copyright (c) 2002 Pavel Machek <pavel@ucw.cz> 762306a36Sopenharmony_ci * Copyright (c) 2001 Patrick Mochel <mochel@osdl.org> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/gfp.h> 1062306a36Sopenharmony_ci#include <linux/smp.h> 1162306a36Sopenharmony_ci#include <linux/suspend.h> 1262306a36Sopenharmony_ci#include <linux/scatterlist.h> 1362306a36Sopenharmony_ci#include <linux/kdebug.h> 1462306a36Sopenharmony_ci#include <linux/cpu.h> 1562306a36Sopenharmony_ci#include <linux/pgtable.h> 1662306a36Sopenharmony_ci#include <linux/types.h> 1762306a36Sopenharmony_ci#include <linux/crc32.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <asm/e820/api.h> 2062306a36Sopenharmony_ci#include <asm/init.h> 2162306a36Sopenharmony_ci#include <asm/proto.h> 2262306a36Sopenharmony_ci#include <asm/page.h> 2362306a36Sopenharmony_ci#include <asm/mtrr.h> 2462306a36Sopenharmony_ci#include <asm/sections.h> 2562306a36Sopenharmony_ci#include <asm/suspend.h> 2662306a36Sopenharmony_ci#include <asm/tlbflush.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Address to jump to in the last phase of restore in order to get to the image 3062306a36Sopenharmony_ci * kernel's text (this value is passed in the image header). 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ciunsigned long restore_jump_address __visible; 3362306a36Sopenharmony_ciunsigned long jump_address_phys; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * Value of the cr3 register from before the hibernation (this value is passed 3762306a36Sopenharmony_ci * in the image header). 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ciunsigned long restore_cr3 __visible; 4062306a36Sopenharmony_ciunsigned long temp_pgt __visible; 4162306a36Sopenharmony_ciunsigned long relocated_restore_code __visible; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/** 4462306a36Sopenharmony_ci * pfn_is_nosave - check if given pfn is in the 'nosave' section 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ciint pfn_is_nosave(unsigned long pfn) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci unsigned long nosave_begin_pfn; 4962306a36Sopenharmony_ci unsigned long nosave_end_pfn; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci nosave_begin_pfn = __pa_symbol(&__nosave_begin) >> PAGE_SHIFT; 5262306a36Sopenharmony_ci nosave_end_pfn = PAGE_ALIGN(__pa_symbol(&__nosave_end)) >> PAGE_SHIFT; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return pfn >= nosave_begin_pfn && pfn < nosave_end_pfn; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct restore_data_record { 5862306a36Sopenharmony_ci unsigned long jump_address; 5962306a36Sopenharmony_ci unsigned long jump_address_phys; 6062306a36Sopenharmony_ci unsigned long cr3; 6162306a36Sopenharmony_ci unsigned long magic; 6262306a36Sopenharmony_ci unsigned long e820_checksum; 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/** 6662306a36Sopenharmony_ci * compute_e820_crc32 - calculate crc32 of a given e820 table 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * @table: the e820 table to be calculated 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci * Return: the resulting checksum 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_cistatic inline u32 compute_e820_crc32(struct e820_table *table) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci int size = offsetof(struct e820_table, entries) + 7562306a36Sopenharmony_ci sizeof(struct e820_entry) * table->nr_entries; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return ~crc32_le(~0, (unsigned char const *)table, size); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#ifdef CONFIG_X86_64 8162306a36Sopenharmony_ci#define RESTORE_MAGIC 0x23456789ABCDEF02UL 8262306a36Sopenharmony_ci#else 8362306a36Sopenharmony_ci#define RESTORE_MAGIC 0x12345679UL 8462306a36Sopenharmony_ci#endif 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/** 8762306a36Sopenharmony_ci * arch_hibernation_header_save - populate the architecture specific part 8862306a36Sopenharmony_ci * of a hibernation image header 8962306a36Sopenharmony_ci * @addr: address to save the data at 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ciint arch_hibernation_header_save(void *addr, unsigned int max_size) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct restore_data_record *rdr = addr; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (max_size < sizeof(struct restore_data_record)) 9662306a36Sopenharmony_ci return -EOVERFLOW; 9762306a36Sopenharmony_ci rdr->magic = RESTORE_MAGIC; 9862306a36Sopenharmony_ci rdr->jump_address = (unsigned long)restore_registers; 9962306a36Sopenharmony_ci rdr->jump_address_phys = __pa_symbol(restore_registers); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* 10262306a36Sopenharmony_ci * The restore code fixes up CR3 and CR4 in the following sequence: 10362306a36Sopenharmony_ci * 10462306a36Sopenharmony_ci * [in hibernation asm] 10562306a36Sopenharmony_ci * 1. CR3 <= temporary page tables 10662306a36Sopenharmony_ci * 2. CR4 <= mmu_cr4_features (from the kernel that restores us) 10762306a36Sopenharmony_ci * 3. CR3 <= rdr->cr3 10862306a36Sopenharmony_ci * 4. CR4 <= mmu_cr4_features (from us, i.e. the image kernel) 10962306a36Sopenharmony_ci * [in restore_processor_state()] 11062306a36Sopenharmony_ci * 5. CR4 <= saved CR4 11162306a36Sopenharmony_ci * 6. CR3 <= saved CR3 11262306a36Sopenharmony_ci * 11362306a36Sopenharmony_ci * Our mmu_cr4_features has CR4.PCIDE=0, and toggling 11462306a36Sopenharmony_ci * CR4.PCIDE while CR3's PCID bits are nonzero is illegal, so 11562306a36Sopenharmony_ci * rdr->cr3 needs to point to valid page tables but must not 11662306a36Sopenharmony_ci * have any of the PCID bits set. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci rdr->cr3 = restore_cr3 & ~CR3_PCID_MASK; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci rdr->e820_checksum = compute_e820_crc32(e820_table_firmware); 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/** 12562306a36Sopenharmony_ci * arch_hibernation_header_restore - read the architecture specific data 12662306a36Sopenharmony_ci * from the hibernation image header 12762306a36Sopenharmony_ci * @addr: address to read the data from 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ciint arch_hibernation_header_restore(void *addr) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct restore_data_record *rdr = addr; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (rdr->magic != RESTORE_MAGIC) { 13462306a36Sopenharmony_ci pr_crit("Unrecognized hibernate image header format!\n"); 13562306a36Sopenharmony_ci return -EINVAL; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci restore_jump_address = rdr->jump_address; 13962306a36Sopenharmony_ci jump_address_phys = rdr->jump_address_phys; 14062306a36Sopenharmony_ci restore_cr3 = rdr->cr3; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (rdr->e820_checksum != compute_e820_crc32(e820_table_firmware)) { 14362306a36Sopenharmony_ci pr_crit("Hibernate inconsistent memory map detected!\n"); 14462306a36Sopenharmony_ci return -ENODEV; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ciint relocate_restore_code(void) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci pgd_t *pgd; 15362306a36Sopenharmony_ci p4d_t *p4d; 15462306a36Sopenharmony_ci pud_t *pud; 15562306a36Sopenharmony_ci pmd_t *pmd; 15662306a36Sopenharmony_ci pte_t *pte; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci relocated_restore_code = get_safe_page(GFP_ATOMIC); 15962306a36Sopenharmony_ci if (!relocated_restore_code) 16062306a36Sopenharmony_ci return -ENOMEM; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci __memcpy((void *)relocated_restore_code, core_restore_code, PAGE_SIZE); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* Make the page containing the relocated code executable */ 16562306a36Sopenharmony_ci pgd = (pgd_t *)__va(read_cr3_pa()) + 16662306a36Sopenharmony_ci pgd_index(relocated_restore_code); 16762306a36Sopenharmony_ci p4d = p4d_offset(pgd, relocated_restore_code); 16862306a36Sopenharmony_ci if (p4d_large(*p4d)) { 16962306a36Sopenharmony_ci set_p4d(p4d, __p4d(p4d_val(*p4d) & ~_PAGE_NX)); 17062306a36Sopenharmony_ci goto out; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci pud = pud_offset(p4d, relocated_restore_code); 17362306a36Sopenharmony_ci if (pud_large(*pud)) { 17462306a36Sopenharmony_ci set_pud(pud, __pud(pud_val(*pud) & ~_PAGE_NX)); 17562306a36Sopenharmony_ci goto out; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci pmd = pmd_offset(pud, relocated_restore_code); 17862306a36Sopenharmony_ci if (pmd_large(*pmd)) { 17962306a36Sopenharmony_ci set_pmd(pmd, __pmd(pmd_val(*pmd) & ~_PAGE_NX)); 18062306a36Sopenharmony_ci goto out; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci pte = pte_offset_kernel(pmd, relocated_restore_code); 18362306a36Sopenharmony_ci set_pte(pte, __pte(pte_val(*pte) & ~_PAGE_NX)); 18462306a36Sopenharmony_ciout: 18562306a36Sopenharmony_ci __flush_tlb_all(); 18662306a36Sopenharmony_ci return 0; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ciint arch_resume_nosmt(void) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci int ret = 0; 19262306a36Sopenharmony_ci /* 19362306a36Sopenharmony_ci * We reached this while coming out of hibernation. This means 19462306a36Sopenharmony_ci * that SMT siblings are sleeping in hlt, as mwait is not safe 19562306a36Sopenharmony_ci * against control transition during resume (see comment in 19662306a36Sopenharmony_ci * hibernate_resume_nonboot_cpu_disable()). 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * If the resumed kernel has SMT disabled, we have to take all the 19962306a36Sopenharmony_ci * SMT siblings out of hlt, and offline them again so that they 20062306a36Sopenharmony_ci * end up in mwait proper. 20162306a36Sopenharmony_ci * 20262306a36Sopenharmony_ci * Called with hotplug disabled. 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci cpu_hotplug_enable(); 20562306a36Sopenharmony_ci if (cpu_smt_control == CPU_SMT_DISABLED || 20662306a36Sopenharmony_ci cpu_smt_control == CPU_SMT_FORCE_DISABLED) { 20762306a36Sopenharmony_ci enum cpuhp_smt_control old = cpu_smt_control; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci ret = cpuhp_smt_enable(); 21062306a36Sopenharmony_ci if (ret) 21162306a36Sopenharmony_ci goto out; 21262306a36Sopenharmony_ci ret = cpuhp_smt_disable(old); 21362306a36Sopenharmony_ci if (ret) 21462306a36Sopenharmony_ci goto out; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ciout: 21762306a36Sopenharmony_ci cpu_hotplug_disable(); 21862306a36Sopenharmony_ci return ret; 21962306a36Sopenharmony_ci} 220