0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/gfp.h>
0010 #include <linux/smp.h>
0011 #include <linux/suspend.h>
0012 #include <linux/scatterlist.h>
0013 #include <linux/kdebug.h>
0014 #include <linux/cpu.h>
0015 #include <linux/pgtable.h>
0016 #include <linux/types.h>
0017 #include <linux/crc32.h>
0018
0019 #include <asm/e820/api.h>
0020 #include <asm/init.h>
0021 #include <asm/proto.h>
0022 #include <asm/page.h>
0023 #include <asm/mtrr.h>
0024 #include <asm/sections.h>
0025 #include <asm/suspend.h>
0026 #include <asm/tlbflush.h>
0027
0028
0029
0030
0031
0032 unsigned long restore_jump_address __visible;
0033 unsigned long jump_address_phys;
0034
0035
0036
0037
0038
0039 unsigned long restore_cr3 __visible;
0040 unsigned long temp_pgt __visible;
0041 unsigned long relocated_restore_code __visible;
0042
0043
0044
0045
0046 int pfn_is_nosave(unsigned long pfn)
0047 {
0048 unsigned long nosave_begin_pfn;
0049 unsigned long nosave_end_pfn;
0050
0051 nosave_begin_pfn = __pa_symbol(&__nosave_begin) >> PAGE_SHIFT;
0052 nosave_end_pfn = PAGE_ALIGN(__pa_symbol(&__nosave_end)) >> PAGE_SHIFT;
0053
0054 return pfn >= nosave_begin_pfn && pfn < nosave_end_pfn;
0055 }
0056
0057 struct restore_data_record {
0058 unsigned long jump_address;
0059 unsigned long jump_address_phys;
0060 unsigned long cr3;
0061 unsigned long magic;
0062 unsigned long e820_checksum;
0063 };
0064
0065
0066
0067
0068
0069
0070
0071
0072 static inline u32 compute_e820_crc32(struct e820_table *table)
0073 {
0074 int size = offsetof(struct e820_table, entries) +
0075 sizeof(struct e820_entry) * table->nr_entries;
0076
0077 return ~crc32_le(~0, (unsigned char const *)table, size);
0078 }
0079
0080 #ifdef CONFIG_X86_64
0081 #define RESTORE_MAGIC 0x23456789ABCDEF02UL
0082 #else
0083 #define RESTORE_MAGIC 0x12345679UL
0084 #endif
0085
0086
0087
0088
0089
0090
0091 int arch_hibernation_header_save(void *addr, unsigned int max_size)
0092 {
0093 struct restore_data_record *rdr = addr;
0094
0095 if (max_size < sizeof(struct restore_data_record))
0096 return -EOVERFLOW;
0097 rdr->magic = RESTORE_MAGIC;
0098 rdr->jump_address = (unsigned long)restore_registers;
0099 rdr->jump_address_phys = __pa_symbol(restore_registers);
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118 rdr->cr3 = restore_cr3 & ~CR3_PCID_MASK;
0119
0120 rdr->e820_checksum = compute_e820_crc32(e820_table_firmware);
0121 return 0;
0122 }
0123
0124
0125
0126
0127
0128
0129 int arch_hibernation_header_restore(void *addr)
0130 {
0131 struct restore_data_record *rdr = addr;
0132
0133 if (rdr->magic != RESTORE_MAGIC) {
0134 pr_crit("Unrecognized hibernate image header format!\n");
0135 return -EINVAL;
0136 }
0137
0138 restore_jump_address = rdr->jump_address;
0139 jump_address_phys = rdr->jump_address_phys;
0140 restore_cr3 = rdr->cr3;
0141
0142 if (rdr->e820_checksum != compute_e820_crc32(e820_table_firmware)) {
0143 pr_crit("Hibernate inconsistent memory map detected!\n");
0144 return -ENODEV;
0145 }
0146
0147 return 0;
0148 }
0149
0150 int relocate_restore_code(void)
0151 {
0152 pgd_t *pgd;
0153 p4d_t *p4d;
0154 pud_t *pud;
0155 pmd_t *pmd;
0156 pte_t *pte;
0157
0158 relocated_restore_code = get_safe_page(GFP_ATOMIC);
0159 if (!relocated_restore_code)
0160 return -ENOMEM;
0161
0162 memcpy((void *)relocated_restore_code, core_restore_code, PAGE_SIZE);
0163
0164
0165 pgd = (pgd_t *)__va(read_cr3_pa()) +
0166 pgd_index(relocated_restore_code);
0167 p4d = p4d_offset(pgd, relocated_restore_code);
0168 if (p4d_large(*p4d)) {
0169 set_p4d(p4d, __p4d(p4d_val(*p4d) & ~_PAGE_NX));
0170 goto out;
0171 }
0172 pud = pud_offset(p4d, relocated_restore_code);
0173 if (pud_large(*pud)) {
0174 set_pud(pud, __pud(pud_val(*pud) & ~_PAGE_NX));
0175 goto out;
0176 }
0177 pmd = pmd_offset(pud, relocated_restore_code);
0178 if (pmd_large(*pmd)) {
0179 set_pmd(pmd, __pmd(pmd_val(*pmd) & ~_PAGE_NX));
0180 goto out;
0181 }
0182 pte = pte_offset_kernel(pmd, relocated_restore_code);
0183 set_pte(pte, __pte(pte_val(*pte) & ~_PAGE_NX));
0184 out:
0185 __flush_tlb_all();
0186 return 0;
0187 }
0188
0189 int arch_resume_nosmt(void)
0190 {
0191 int ret = 0;
0192
0193
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204 cpu_hotplug_enable();
0205 if (cpu_smt_control == CPU_SMT_DISABLED ||
0206 cpu_smt_control == CPU_SMT_FORCE_DISABLED) {
0207 enum cpuhp_smt_control old = cpu_smt_control;
0208
0209 ret = cpuhp_smt_enable();
0210 if (ret)
0211 goto out;
0212 ret = cpuhp_smt_disable(old);
0213 if (ret)
0214 goto out;
0215 }
0216 out:
0217 cpu_hotplug_disable();
0218 return ret;
0219 }