xref: /OK3568_Linux_fs/kernel/arch/x86/power/hibernate.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Hibernation support for x86
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (c) 2007 Rafael J. Wysocki <rjw@sisk.pl>
6*4882a593Smuzhiyun  * Copyright (c) 2002 Pavel Machek <pavel@ucw.cz>
7*4882a593Smuzhiyun  * Copyright (c) 2001 Patrick Mochel <mochel@osdl.org>
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun #include <linux/gfp.h>
10*4882a593Smuzhiyun #include <linux/smp.h>
11*4882a593Smuzhiyun #include <linux/suspend.h>
12*4882a593Smuzhiyun #include <linux/scatterlist.h>
13*4882a593Smuzhiyun #include <linux/kdebug.h>
14*4882a593Smuzhiyun #include <linux/cpu.h>
15*4882a593Smuzhiyun #include <linux/pgtable.h>
16*4882a593Smuzhiyun #include <linux/types.h>
17*4882a593Smuzhiyun #include <linux/crc32.h>
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #include <asm/e820/api.h>
20*4882a593Smuzhiyun #include <asm/init.h>
21*4882a593Smuzhiyun #include <asm/proto.h>
22*4882a593Smuzhiyun #include <asm/page.h>
23*4882a593Smuzhiyun #include <asm/mtrr.h>
24*4882a593Smuzhiyun #include <asm/sections.h>
25*4882a593Smuzhiyun #include <asm/suspend.h>
26*4882a593Smuzhiyun #include <asm/tlbflush.h>
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun /*
29*4882a593Smuzhiyun  * Address to jump to in the last phase of restore in order to get to the image
30*4882a593Smuzhiyun  * kernel's text (this value is passed in the image header).
31*4882a593Smuzhiyun  */
32*4882a593Smuzhiyun unsigned long restore_jump_address __visible;
33*4882a593Smuzhiyun unsigned long jump_address_phys;
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun /*
36*4882a593Smuzhiyun  * Value of the cr3 register from before the hibernation (this value is passed
37*4882a593Smuzhiyun  * in the image header).
38*4882a593Smuzhiyun  */
39*4882a593Smuzhiyun unsigned long restore_cr3 __visible;
40*4882a593Smuzhiyun unsigned long temp_pgt __visible;
41*4882a593Smuzhiyun unsigned long relocated_restore_code __visible;
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun /**
44*4882a593Smuzhiyun  *	pfn_is_nosave - check if given pfn is in the 'nosave' section
45*4882a593Smuzhiyun  */
pfn_is_nosave(unsigned long pfn)46*4882a593Smuzhiyun int pfn_is_nosave(unsigned long pfn)
47*4882a593Smuzhiyun {
48*4882a593Smuzhiyun 	unsigned long nosave_begin_pfn;
49*4882a593Smuzhiyun 	unsigned long nosave_end_pfn;
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	nosave_begin_pfn = __pa_symbol(&__nosave_begin) >> PAGE_SHIFT;
52*4882a593Smuzhiyun 	nosave_end_pfn = PAGE_ALIGN(__pa_symbol(&__nosave_end)) >> PAGE_SHIFT;
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun 	return pfn >= nosave_begin_pfn && pfn < nosave_end_pfn;
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun struct restore_data_record {
58*4882a593Smuzhiyun 	unsigned long jump_address;
59*4882a593Smuzhiyun 	unsigned long jump_address_phys;
60*4882a593Smuzhiyun 	unsigned long cr3;
61*4882a593Smuzhiyun 	unsigned long magic;
62*4882a593Smuzhiyun 	unsigned long e820_checksum;
63*4882a593Smuzhiyun };
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun /**
66*4882a593Smuzhiyun  * compute_e820_crc32 - calculate crc32 of a given e820 table
67*4882a593Smuzhiyun  *
68*4882a593Smuzhiyun  * @table: the e820 table to be calculated
69*4882a593Smuzhiyun  *
70*4882a593Smuzhiyun  * Return: the resulting checksum
71*4882a593Smuzhiyun  */
compute_e820_crc32(struct e820_table * table)72*4882a593Smuzhiyun static inline u32 compute_e820_crc32(struct e820_table *table)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun 	int size = offsetof(struct e820_table, entries) +
75*4882a593Smuzhiyun 		sizeof(struct e820_entry) * table->nr_entries;
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 	return ~crc32_le(~0, (unsigned char const *)table, size);
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun #ifdef CONFIG_X86_64
81*4882a593Smuzhiyun #define RESTORE_MAGIC	0x23456789ABCDEF02UL
82*4882a593Smuzhiyun #else
83*4882a593Smuzhiyun #define RESTORE_MAGIC	0x12345679UL
84*4882a593Smuzhiyun #endif
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun /**
87*4882a593Smuzhiyun  *	arch_hibernation_header_save - populate the architecture specific part
88*4882a593Smuzhiyun  *		of a hibernation image header
89*4882a593Smuzhiyun  *	@addr: address to save the data at
90*4882a593Smuzhiyun  */
arch_hibernation_header_save(void * addr,unsigned int max_size)91*4882a593Smuzhiyun int arch_hibernation_header_save(void *addr, unsigned int max_size)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun 	struct restore_data_record *rdr = addr;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	if (max_size < sizeof(struct restore_data_record))
96*4882a593Smuzhiyun 		return -EOVERFLOW;
97*4882a593Smuzhiyun 	rdr->magic = RESTORE_MAGIC;
98*4882a593Smuzhiyun 	rdr->jump_address = (unsigned long)restore_registers;
99*4882a593Smuzhiyun 	rdr->jump_address_phys = __pa_symbol(restore_registers);
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	/*
102*4882a593Smuzhiyun 	 * The restore code fixes up CR3 and CR4 in the following sequence:
103*4882a593Smuzhiyun 	 *
104*4882a593Smuzhiyun 	 * [in hibernation asm]
105*4882a593Smuzhiyun 	 * 1. CR3 <= temporary page tables
106*4882a593Smuzhiyun 	 * 2. CR4 <= mmu_cr4_features (from the kernel that restores us)
107*4882a593Smuzhiyun 	 * 3. CR3 <= rdr->cr3
108*4882a593Smuzhiyun 	 * 4. CR4 <= mmu_cr4_features (from us, i.e. the image kernel)
109*4882a593Smuzhiyun 	 * [in restore_processor_state()]
110*4882a593Smuzhiyun 	 * 5. CR4 <= saved CR4
111*4882a593Smuzhiyun 	 * 6. CR3 <= saved CR3
112*4882a593Smuzhiyun 	 *
113*4882a593Smuzhiyun 	 * Our mmu_cr4_features has CR4.PCIDE=0, and toggling
114*4882a593Smuzhiyun 	 * CR4.PCIDE while CR3's PCID bits are nonzero is illegal, so
115*4882a593Smuzhiyun 	 * rdr->cr3 needs to point to valid page tables but must not
116*4882a593Smuzhiyun 	 * have any of the PCID bits set.
117*4882a593Smuzhiyun 	 */
118*4882a593Smuzhiyun 	rdr->cr3 = restore_cr3 & ~CR3_PCID_MASK;
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	rdr->e820_checksum = compute_e820_crc32(e820_table_firmware);
121*4882a593Smuzhiyun 	return 0;
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun /**
125*4882a593Smuzhiyun  *	arch_hibernation_header_restore - read the architecture specific data
126*4882a593Smuzhiyun  *		from the hibernation image header
127*4882a593Smuzhiyun  *	@addr: address to read the data from
128*4882a593Smuzhiyun  */
arch_hibernation_header_restore(void * addr)129*4882a593Smuzhiyun int arch_hibernation_header_restore(void *addr)
130*4882a593Smuzhiyun {
131*4882a593Smuzhiyun 	struct restore_data_record *rdr = addr;
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	if (rdr->magic != RESTORE_MAGIC) {
134*4882a593Smuzhiyun 		pr_crit("Unrecognized hibernate image header format!\n");
135*4882a593Smuzhiyun 		return -EINVAL;
136*4882a593Smuzhiyun 	}
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	restore_jump_address = rdr->jump_address;
139*4882a593Smuzhiyun 	jump_address_phys = rdr->jump_address_phys;
140*4882a593Smuzhiyun 	restore_cr3 = rdr->cr3;
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	if (rdr->e820_checksum != compute_e820_crc32(e820_table_firmware)) {
143*4882a593Smuzhiyun 		pr_crit("Hibernate inconsistent memory map detected!\n");
144*4882a593Smuzhiyun 		return -ENODEV;
145*4882a593Smuzhiyun 	}
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	return 0;
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun 
relocate_restore_code(void)150*4882a593Smuzhiyun int relocate_restore_code(void)
151*4882a593Smuzhiyun {
152*4882a593Smuzhiyun 	pgd_t *pgd;
153*4882a593Smuzhiyun 	p4d_t *p4d;
154*4882a593Smuzhiyun 	pud_t *pud;
155*4882a593Smuzhiyun 	pmd_t *pmd;
156*4882a593Smuzhiyun 	pte_t *pte;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	relocated_restore_code = get_safe_page(GFP_ATOMIC);
159*4882a593Smuzhiyun 	if (!relocated_restore_code)
160*4882a593Smuzhiyun 		return -ENOMEM;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	memcpy((void *)relocated_restore_code, core_restore_code, PAGE_SIZE);
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	/* Make the page containing the relocated code executable */
165*4882a593Smuzhiyun 	pgd = (pgd_t *)__va(read_cr3_pa()) +
166*4882a593Smuzhiyun 		pgd_index(relocated_restore_code);
167*4882a593Smuzhiyun 	p4d = p4d_offset(pgd, relocated_restore_code);
168*4882a593Smuzhiyun 	if (p4d_large(*p4d)) {
169*4882a593Smuzhiyun 		set_p4d(p4d, __p4d(p4d_val(*p4d) & ~_PAGE_NX));
170*4882a593Smuzhiyun 		goto out;
171*4882a593Smuzhiyun 	}
172*4882a593Smuzhiyun 	pud = pud_offset(p4d, relocated_restore_code);
173*4882a593Smuzhiyun 	if (pud_large(*pud)) {
174*4882a593Smuzhiyun 		set_pud(pud, __pud(pud_val(*pud) & ~_PAGE_NX));
175*4882a593Smuzhiyun 		goto out;
176*4882a593Smuzhiyun 	}
177*4882a593Smuzhiyun 	pmd = pmd_offset(pud, relocated_restore_code);
178*4882a593Smuzhiyun 	if (pmd_large(*pmd)) {
179*4882a593Smuzhiyun 		set_pmd(pmd, __pmd(pmd_val(*pmd) & ~_PAGE_NX));
180*4882a593Smuzhiyun 		goto out;
181*4882a593Smuzhiyun 	}
182*4882a593Smuzhiyun 	pte = pte_offset_kernel(pmd, relocated_restore_code);
183*4882a593Smuzhiyun 	set_pte(pte, __pte(pte_val(*pte) & ~_PAGE_NX));
184*4882a593Smuzhiyun out:
185*4882a593Smuzhiyun 	__flush_tlb_all();
186*4882a593Smuzhiyun 	return 0;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun 
arch_resume_nosmt(void)189*4882a593Smuzhiyun int arch_resume_nosmt(void)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun 	int ret = 0;
192*4882a593Smuzhiyun 	/*
193*4882a593Smuzhiyun 	 * We reached this while coming out of hibernation. This means
194*4882a593Smuzhiyun 	 * that SMT siblings are sleeping in hlt, as mwait is not safe
195*4882a593Smuzhiyun 	 * against control transition during resume (see comment in
196*4882a593Smuzhiyun 	 * hibernate_resume_nonboot_cpu_disable()).
197*4882a593Smuzhiyun 	 *
198*4882a593Smuzhiyun 	 * If the resumed kernel has SMT disabled, we have to take all the
199*4882a593Smuzhiyun 	 * SMT siblings out of hlt, and offline them again so that they
200*4882a593Smuzhiyun 	 * end up in mwait proper.
201*4882a593Smuzhiyun 	 *
202*4882a593Smuzhiyun 	 * Called with hotplug disabled.
203*4882a593Smuzhiyun 	 */
204*4882a593Smuzhiyun 	cpu_hotplug_enable();
205*4882a593Smuzhiyun 	if (cpu_smt_control == CPU_SMT_DISABLED ||
206*4882a593Smuzhiyun 			cpu_smt_control == CPU_SMT_FORCE_DISABLED) {
207*4882a593Smuzhiyun 		enum cpuhp_smt_control old = cpu_smt_control;
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 		ret = cpuhp_smt_enable();
210*4882a593Smuzhiyun 		if (ret)
211*4882a593Smuzhiyun 			goto out;
212*4882a593Smuzhiyun 		ret = cpuhp_smt_disable(old);
213*4882a593Smuzhiyun 		if (ret)
214*4882a593Smuzhiyun 			goto out;
215*4882a593Smuzhiyun 	}
216*4882a593Smuzhiyun out:
217*4882a593Smuzhiyun 	cpu_hotplug_disable();
218*4882a593Smuzhiyun 	return ret;
219*4882a593Smuzhiyun }
220