Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0 2 : : /* 3 : : * sleep.c - x86-specific ACPI sleep support. 4 : : * 5 : : * Copyright (C) 2001-2003 Patrick Mochel 6 : : * Copyright (C) 2001-2003 Pavel Machek <pavel@ucw.cz> 7 : : */ 8 : : 9 : : #include <linux/acpi.h> 10 : : #include <linux/memblock.h> 11 : : #include <linux/dmi.h> 12 : : #include <linux/cpumask.h> 13 : : #include <asm/segment.h> 14 : : #include <asm/desc.h> 15 : : #include <asm/pgtable.h> 16 : : #include <asm/cacheflush.h> 17 : : #include <asm/realmode.h> 18 : : 19 : : #include <linux/ftrace.h> 20 : : #include "../../realmode/rm/wakeup.h" 21 : : #include "sleep.h" 22 : : 23 : : unsigned long acpi_realmode_flags; 24 : : 25 : : #if defined(CONFIG_SMP) && defined(CONFIG_64BIT) 26 : : static char temp_stack[4096]; 27 : : #endif 28 : : 29 : : /** 30 : : * acpi_get_wakeup_address - provide physical address for S3 wakeup 31 : : * 32 : : * Returns the physical address where the kernel should be resumed after the 33 : : * system awakes from S3, e.g. for programming into the firmware waking vector. 34 : : */ 35 : 0 : unsigned long acpi_get_wakeup_address(void) 36 : : { 37 : 0 : return ((unsigned long)(real_mode_header->wakeup_start)); 38 : : } 39 : : 40 : : /** 41 : : * x86_acpi_enter_sleep_state - enter sleep state 42 : : * @state: Sleep state to enter. 43 : : * 44 : : * Wrapper around acpi_enter_sleep_state() to be called by assmebly. 45 : : */ 46 : 0 : acpi_status asmlinkage __visible x86_acpi_enter_sleep_state(u8 state) 47 : : { 48 : 0 : return acpi_enter_sleep_state(state); 49 : : } 50 : : 51 : : /** 52 : : * x86_acpi_suspend_lowlevel - save kernel state 53 : : * 54 : : * Create an identity mapped page table and copy the wakeup routine to 55 : : * low memory. 56 : : */ 57 : 0 : int x86_acpi_suspend_lowlevel(void) 58 : : { 59 : 0 : struct wakeup_header *header = 60 : 0 : (struct wakeup_header *) __va(real_mode_header->wakeup_header); 61 : : 62 [ # # ]: 0 : if (header->signature != WAKEUP_HEADER_SIGNATURE) { 63 : 0 : printk(KERN_ERR "wakeup header does not match\n"); 64 : 0 : return -EINVAL; 65 : : } 66 : : 67 : 0 : header->video_mode = saved_video_mode; 68 : : 69 : 0 : header->pmode_behavior = 0; 70 : : 71 : : #ifndef CONFIG_64BIT 72 : : native_store_gdt((struct desc_ptr *)&header->pmode_gdt); 73 : : 74 : : /* 75 : : * We have to check that we can write back the value, and not 76 : : * just read it. At least on 90 nm Pentium M (Family 6, Model 77 : : * 13), reading an invalid MSR is not guaranteed to trap, see 78 : : * Erratum X4 in "Intel Pentium M Processor on 90 nm Process 79 : : * with 2-MB L2 Cache and IntelĀ® Processor A100 and A110 on 90 80 : : * nm process with 512-KB L2 Cache Specification Update". 81 : : */ 82 : : if (!rdmsr_safe(MSR_EFER, 83 : : &header->pmode_efer_low, 84 : : &header->pmode_efer_high) && 85 : : !wrmsr_safe(MSR_EFER, 86 : : header->pmode_efer_low, 87 : : header->pmode_efer_high)) 88 : : header->pmode_behavior |= (1 << WAKEUP_BEHAVIOR_RESTORE_EFER); 89 : : #endif /* !CONFIG_64BIT */ 90 : : 91 : 0 : header->pmode_cr0 = read_cr0(); 92 [ # # ]: 0 : if (__this_cpu_read(cpu_info.cpuid_level) >= 0) { 93 : 0 : header->pmode_cr4 = __read_cr4(); 94 : 0 : header->pmode_behavior |= (1 << WAKEUP_BEHAVIOR_RESTORE_CR4); 95 : : } 96 [ # # ]: 0 : if (!rdmsr_safe(MSR_IA32_MISC_ENABLE, 97 : : &header->pmode_misc_en_low, 98 [ # # ]: 0 : &header->pmode_misc_en_high) && 99 : : !wrmsr_safe(MSR_IA32_MISC_ENABLE, 100 : : header->pmode_misc_en_low, 101 : : header->pmode_misc_en_high)) 102 : 0 : header->pmode_behavior |= 103 : : (1 << WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE); 104 : 0 : header->realmode_flags = acpi_realmode_flags; 105 : 0 : header->real_magic = 0x12345678; 106 : : 107 : : #ifndef CONFIG_64BIT 108 : : header->pmode_entry = (u32)&wakeup_pmode_return; 109 : : header->pmode_cr3 = (u32)__pa_symbol(initial_page_table); 110 : : saved_magic = 0x12345678; 111 : : #else /* CONFIG_64BIT */ 112 : : #ifdef CONFIG_SMP 113 : 0 : initial_stack = (unsigned long)temp_stack + sizeof(temp_stack); 114 : 0 : early_gdt_descr.address = 115 : 0 : (unsigned long)get_cpu_gdt_rw(smp_processor_id()); 116 : 0 : initial_gs = per_cpu_offset(smp_processor_id()); 117 : : #endif 118 : 0 : initial_code = (unsigned long)wakeup_long64; 119 : 0 : saved_magic = 0x123456789abcdef0L; 120 : : #endif /* CONFIG_64BIT */ 121 : : 122 : : /* 123 : : * Pause/unpause graph tracing around do_suspend_lowlevel as it has 124 : : * inconsistent call/return info after it jumps to the wakeup vector. 125 : : */ 126 : 0 : pause_graph_tracing(); 127 : 0 : do_suspend_lowlevel(); 128 : 0 : unpause_graph_tracing(); 129 : 0 : return 0; 130 : : } 131 : : 132 : 0 : static int __init acpi_sleep_setup(char *str) 133 : : { 134 [ # # # # ]: 0 : while ((str != NULL) && (*str != '\0')) { 135 [ # # ]: 0 : if (strncmp(str, "s3_bios", 7) == 0) 136 : 0 : acpi_realmode_flags |= 1; 137 [ # # ]: 0 : if (strncmp(str, "s3_mode", 7) == 0) 138 : 0 : acpi_realmode_flags |= 2; 139 [ # # ]: 0 : if (strncmp(str, "s3_beep", 7) == 0) 140 : 0 : acpi_realmode_flags |= 4; 141 : : #ifdef CONFIG_HIBERNATION 142 [ # # ]: 0 : if (strncmp(str, "s4_nohwsig", 10) == 0) 143 : 0 : acpi_no_s4_hw_signature(); 144 : : #endif 145 [ # # ]: 0 : if (strncmp(str, "nonvs", 5) == 0) 146 : 0 : acpi_nvs_nosave(); 147 [ # # ]: 0 : if (strncmp(str, "nonvs_s3", 8) == 0) 148 : 0 : acpi_nvs_nosave_s3(); 149 [ # # ]: 0 : if (strncmp(str, "old_ordering", 12) == 0) 150 : 0 : acpi_old_suspend_ordering(); 151 [ # # ]: 0 : if (strncmp(str, "nobl", 4) == 0) 152 : 0 : acpi_sleep_no_blacklist(); 153 : 0 : str = strchr(str, ','); 154 [ # # ]: 0 : if (str != NULL) 155 : 0 : str += strspn(str, ", \t"); 156 : : } 157 : 0 : return 1; 158 : : } 159 : : 160 : : __setup("acpi_sleep=", acpi_sleep_setup);