Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | // SPDX-License-Identifier: GPL-2.0 /* * sleep.c - x86-specific ACPI sleep support. * * Copyright (C) 2001-2003 Patrick Mochel * Copyright (C) 2001-2003 Pavel Machek <pavel@ucw.cz> */ #include <linux/acpi.h> #include <linux/memblock.h> #include <linux/dmi.h> #include <linux/cpumask.h> #include <linux/pgtable.h> #include <asm/segment.h> #include <asm/desc.h> #include <asm/cacheflush.h> #include <asm/realmode.h> #include <asm/hypervisor.h> #include <asm/smp.h> #include <linux/ftrace.h> #include "../../realmode/rm/wakeup.h" #include "sleep.h" unsigned long acpi_realmode_flags; #if defined(CONFIG_SMP) && defined(CONFIG_64BIT) static char temp_stack[4096]; #endif /** * acpi_get_wakeup_address - provide physical address for S3 wakeup * * Returns the physical address where the kernel should be resumed after the * system awakes from S3, e.g. for programming into the firmware waking vector. */ unsigned long acpi_get_wakeup_address(void) { return ((unsigned long)(real_mode_header->wakeup_start)); } /** * x86_acpi_enter_sleep_state - enter sleep state * @state: Sleep state to enter. * * Wrapper around acpi_enter_sleep_state() to be called by assembly. */ asmlinkage acpi_status __visible x86_acpi_enter_sleep_state(u8 state) { return acpi_enter_sleep_state(state); } /** * x86_acpi_suspend_lowlevel - save kernel state * * Create an identity mapped page table and copy the wakeup routine to * low memory. */ int x86_acpi_suspend_lowlevel(void) { struct wakeup_header *header = (struct wakeup_header *) __va(real_mode_header->wakeup_header); if (header->signature != WAKEUP_HEADER_SIGNATURE) { printk(KERN_ERR "wakeup header does not match\n"); return -EINVAL; } header->video_mode = saved_video_mode; header->pmode_behavior = 0; #ifndef CONFIG_64BIT native_store_gdt((struct desc_ptr *)&header->pmode_gdt); /* * We have to check that we can write back the value, and not * just read it. At least on 90 nm Pentium M (Family 6, Model * 13), reading an invalid MSR is not guaranteed to trap, see * Erratum X4 in "Intel Pentium M Processor on 90 nm Process * with 2-MB L2 Cache and IntelĀ® Processor A100 and A110 on 90 * nm process with 512-KB L2 Cache Specification Update". */ if (!rdmsr_safe(MSR_EFER, &header->pmode_efer_low, &header->pmode_efer_high) && !wrmsr_safe(MSR_EFER, header->pmode_efer_low, header->pmode_efer_high)) header->pmode_behavior |= (1 << WAKEUP_BEHAVIOR_RESTORE_EFER); #endif /* !CONFIG_64BIT */ header->pmode_cr0 = read_cr0(); if (__this_cpu_read(cpu_info.cpuid_level) >= 0) { header->pmode_cr4 = __read_cr4(); header->pmode_behavior |= (1 << WAKEUP_BEHAVIOR_RESTORE_CR4); } if (!rdmsr_safe(MSR_IA32_MISC_ENABLE, &header->pmode_misc_en_low, &header->pmode_misc_en_high) && !wrmsr_safe(MSR_IA32_MISC_ENABLE, header->pmode_misc_en_low, header->pmode_misc_en_high)) header->pmode_behavior |= (1 << WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE); header->realmode_flags = acpi_realmode_flags; header->real_magic = 0x12345678; #ifndef CONFIG_64BIT header->pmode_entry = (u32)&wakeup_pmode_return; header->pmode_cr3 = (u32)__pa_symbol(initial_page_table); saved_magic = 0x12345678; #else /* CONFIG_64BIT */ #ifdef CONFIG_SMP /* * As each CPU starts up, it will find its own stack pointer * from its current_task->thread.sp. Typically that will be * the idle thread for a newly-started AP, or even the boot * CPU which will find it set to &init_task in the static * per-cpu data. * * Make the resuming CPU use the temporary stack at startup * by setting current->thread.sp to point to that. The true * %rsp will be restored with the rest of the CPU context, * by do_suspend_lowlevel(). And unwinders don't care about * the abuse of ->thread.sp because it's a dead variable * while the thread is running on the CPU anyway; the true * value is in the actual %rsp register. */ current->thread.sp = (unsigned long)temp_stack + sizeof(temp_stack); /* * Ensure the CPU knows which one it is when it comes back, if * it isn't in parallel mode and expected to work that out for * itself. */ if (!(smpboot_control & STARTUP_PARALLEL_MASK)) smpboot_control = smp_processor_id(); #endif initial_code = (unsigned long)wakeup_long64; saved_magic = 0x123456789abcdef0L; #endif /* CONFIG_64BIT */ /* * Pause/unpause graph tracing around do_suspend_lowlevel as it has * inconsistent call/return info after it jumps to the wakeup vector. */ pause_graph_tracing(); do_suspend_lowlevel(); unpause_graph_tracing(); return 0; } static int __init acpi_sleep_setup(char *str) { while ((str != NULL) && (*str != '\0')) { if (strncmp(str, "s3_bios", 7) == 0) acpi_realmode_flags |= 1; if (strncmp(str, "s3_mode", 7) == 0) acpi_realmode_flags |= 2; if (strncmp(str, "s3_beep", 7) == 0) acpi_realmode_flags |= 4; #ifdef CONFIG_HIBERNATION if (strncmp(str, "s4_hwsig", 8) == 0) acpi_check_s4_hw_signature = 1; if (strncmp(str, "s4_nohwsig", 10) == 0) acpi_check_s4_hw_signature = 0; #endif if (strncmp(str, "nonvs", 5) == 0) acpi_nvs_nosave(); if (strncmp(str, "nonvs_s3", 8) == 0) acpi_nvs_nosave_s3(); if (strncmp(str, "old_ordering", 12) == 0) acpi_old_suspend_ordering(); if (strncmp(str, "nobl", 4) == 0) acpi_sleep_no_blacklist(); str = strchr(str, ','); if (str != NULL) str += strspn(str, ", \t"); } return 1; } __setup("acpi_sleep=", acpi_sleep_setup); #if defined(CONFIG_HIBERNATION) && defined(CONFIG_HYPERVISOR_GUEST) static int __init init_s4_sigcheck(void) { /* * If running on a hypervisor, honour the ACPI specification * by default and trigger a clean reboot when the hardware * signature in FACS is changed after hibernation. */ if (acpi_check_s4_hw_signature == -1 && !hypervisor_is_type(X86_HYPER_NATIVE)) acpi_check_s4_hw_signature = 1; return 0; } /* This must happen before acpi_init() which is a subsys initcall */ arch_initcall(init_s4_sigcheck); #endif |