// 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 */ #include #include #include #include #include #include #include #include #include #include #include #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 /** * x86_acpi_enter_sleep_state - enter sleep state * @state: Sleep state to enter. * * Wrapper around acpi_enter_sleep_state() to be called by assmebly. */ acpi_status asmlinkage __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 initial_stack = (unsigned long)temp_stack + sizeof(temp_stack); early_gdt_descr.address = (unsigned long)get_cpu_gdt_rw(smp_processor_id()); initial_gs = per_cpu_offset(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_nohwsig", 10) == 0) acpi_no_s4_hw_signature(); #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(); str = strchr(str, ','); if (str != NULL) str += strspn(str, ", \t"); } return 1; } __setup("acpi_sleep=", acpi_sleep_setup);