14fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek/*
24fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek * sleep.c - x86-specific ACPI sleep support.
34fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek *
44fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek *  Copyright (C) 2001-2003 Patrick Mochel
5a2531293dbb7608fa672ff28efe3ab4027917a2fPavel Machek *  Copyright (C) 2001-2003 Pavel Machek <pavel@ucw.cz>
64fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek */
74fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek
84fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek#include <linux/acpi.h>
94fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek#include <linux/bootmem.h>
10a9ce6bc15100023b411f8117e53a016d61889800Yinghai Lu#include <linux/memblock.h>
114fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek#include <linux/dmi.h>
124fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek#include <linux/cpumask.h>
134fdf08b5bf8d449cc9897395895157c6ff8ddc41H. Peter Anvin#include <asm/segment.h>
143038edabf48f01421c621cb77a712b446d3a5d67Rafael J. Wysocki#include <asm/desc.h>
15b40827fa7268fda8a62490728a61c2856f33830bBorislav Petkov#include <asm/pgtable.h>
16d344e38b2c151ca5e5e39f562017127e93912528H. Peter Anvin#include <asm/cacheflush.h>
17c9b77ccb52a5c77233b0e557b7d4417b00ef4012Jarkko Sakkinen#include <asm/realmode.h>
18b40827fa7268fda8a62490728a61c2856f33830bBorislav Petkov
19c4845474a01f699966272536e8416222e3f2d2cbJarkko Sakkinen#include "../../realmode/rm/wakeup.h"
20e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek#include "sleep.h"
214fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek
224fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machekunsigned long acpi_realmode_flags;
234fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek
249744f5a32853642f8ed0749a1c9ed8cf9c9c9dc4Marcin Slusarz#if defined(CONFIG_SMP) && defined(CONFIG_64BIT)
255000cadcf3188e935dae28c4fc7e24639704ea55Matt Mackallstatic char temp_stack[4096];
26e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek#endif
274fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek
284fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek/**
2940bce100cafb945f1fb5475a70628b4379c74f38Lv Zheng * x86_acpi_enter_sleep_state - enter sleep state
3040bce100cafb945f1fb5475a70628b4379c74f38Lv Zheng * @state: Sleep state to enter.
3140bce100cafb945f1fb5475a70628b4379c74f38Lv Zheng *
3240bce100cafb945f1fb5475a70628b4379c74f38Lv Zheng * Wrapper around acpi_enter_sleep_state() to be called by assmebly.
3340bce100cafb945f1fb5475a70628b4379c74f38Lv Zheng */
342605fc216fa492f9e7c488bdc7f687cd6dcc703bAndi Kleenacpi_status asmlinkage __visible x86_acpi_enter_sleep_state(u8 state)
3540bce100cafb945f1fb5475a70628b4379c74f38Lv Zheng{
3640bce100cafb945f1fb5475a70628b4379c74f38Lv Zheng	return acpi_enter_sleep_state(state);
3740bce100cafb945f1fb5475a70628b4379c74f38Lv Zheng}
3840bce100cafb945f1fb5475a70628b4379c74f38Lv Zheng
3940bce100cafb945f1fb5475a70628b4379c74f38Lv Zheng/**
40d6a77ead21b69c395ca6d09a066ededfac601bccKonrad Rzeszutek Wilk * x86_acpi_suspend_lowlevel - save kernel state
414fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek *
424fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek * Create an identity mapped page table and copy the wakeup routine to
434fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek * low memory.
444fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek */
45d6a77ead21b69c395ca6d09a066ededfac601bccKonrad Rzeszutek Wilkint x86_acpi_suspend_lowlevel(void)
464fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek{
47c9b77ccb52a5c77233b0e557b7d4417b00ef4012Jarkko Sakkinen	struct wakeup_header *header =
48b429dbf6e866bd6dadb56fae66f61f611cde57ffJarkko Sakkinen		(struct wakeup_header *) __va(real_mode_header->wakeup_header);
49e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek
50d1ee433539ea5963a8f946f3428b335d1c5fdb20H. Peter Anvin	if (header->signature != WAKEUP_HEADER_SIGNATURE) {
51e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek		printk(KERN_ERR "wakeup header does not match\n");
52e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek		return -EINVAL;
53e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek	}
54e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek
55e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek	header->video_mode = saved_video_mode;
56e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek
5773201dbec64aebf6b0dca855b523f437972dc7bbH. Peter Anvin	header->pmode_behavior = 0;
5873201dbec64aebf6b0dca855b523f437972dc7bbH. Peter Anvin
59e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek#ifndef CONFIG_64BIT
60357d122670937c35b33d99c46356ef2b63182a1fKonrad Rzeszutek Wilk	native_store_gdt((struct desc_ptr *)&header->pmode_gdt);
61e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek
625ff560fd48d5b3d82fa0c3aff625c9da1a301911H. Peter Anvin	/*
635ff560fd48d5b3d82fa0c3aff625c9da1a301911H. Peter Anvin	 * We have to check that we can write back the value, and not
645ff560fd48d5b3d82fa0c3aff625c9da1a301911H. Peter Anvin	 * just read it.  At least on 90 nm Pentium M (Family 6, Model
655ff560fd48d5b3d82fa0c3aff625c9da1a301911H. Peter Anvin	 * 13), reading an invalid MSR is not guaranteed to trap, see
665ff560fd48d5b3d82fa0c3aff625c9da1a301911H. Peter Anvin	 * Erratum X4 in "Intel Pentium M Processor on 90 nm Process
675ff560fd48d5b3d82fa0c3aff625c9da1a301911H. Peter Anvin	 * with 2-MB L2 Cache and Intel® Processor A100 and A110 on 90
685ff560fd48d5b3d82fa0c3aff625c9da1a301911H. Peter Anvin	 * nm process with 512-KB L2 Cache Specification Update".
695ff560fd48d5b3d82fa0c3aff625c9da1a301911H. Peter Anvin	 */
7073201dbec64aebf6b0dca855b523f437972dc7bbH. Peter Anvin	if (!rdmsr_safe(MSR_EFER,
7173201dbec64aebf6b0dca855b523f437972dc7bbH. Peter Anvin			&header->pmode_efer_low,
725ff560fd48d5b3d82fa0c3aff625c9da1a301911H. Peter Anvin			&header->pmode_efer_high) &&
735ff560fd48d5b3d82fa0c3aff625c9da1a301911H. Peter Anvin	    !wrmsr_safe(MSR_EFER,
745ff560fd48d5b3d82fa0c3aff625c9da1a301911H. Peter Anvin			header->pmode_efer_low,
755ff560fd48d5b3d82fa0c3aff625c9da1a301911H. Peter Anvin			header->pmode_efer_high))
7673201dbec64aebf6b0dca855b523f437972dc7bbH. Peter Anvin		header->pmode_behavior |= (1 << WAKEUP_BEHAVIOR_RESTORE_EFER);
77e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek#endif /* !CONFIG_64BIT */
78e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek
79e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek	header->pmode_cr0 = read_cr0();
8073201dbec64aebf6b0dca855b523f437972dc7bbH. Peter Anvin	if (__this_cpu_read(cpu_info.cpuid_level) >= 0) {
8173201dbec64aebf6b0dca855b523f437972dc7bbH. Peter Anvin		header->pmode_cr4 = read_cr4();
8273201dbec64aebf6b0dca855b523f437972dc7bbH. Peter Anvin		header->pmode_behavior |= (1 << WAKEUP_BEHAVIOR_RESTORE_CR4);
8373201dbec64aebf6b0dca855b523f437972dc7bbH. Peter Anvin	}
847a3136666bc0f0419f7aaa7b1fabb4b0e0a7fb76Kees Cook	if (!rdmsr_safe(MSR_IA32_MISC_ENABLE,
857a3136666bc0f0419f7aaa7b1fabb4b0e0a7fb76Kees Cook			&header->pmode_misc_en_low,
865ff560fd48d5b3d82fa0c3aff625c9da1a301911H. Peter Anvin			&header->pmode_misc_en_high) &&
875ff560fd48d5b3d82fa0c3aff625c9da1a301911H. Peter Anvin	    !wrmsr_safe(MSR_IA32_MISC_ENABLE,
885ff560fd48d5b3d82fa0c3aff625c9da1a301911H. Peter Anvin			header->pmode_misc_en_low,
895ff560fd48d5b3d82fa0c3aff625c9da1a301911H. Peter Anvin			header->pmode_misc_en_high))
907a3136666bc0f0419f7aaa7b1fabb4b0e0a7fb76Kees Cook		header->pmode_behavior |=
917a3136666bc0f0419f7aaa7b1fabb4b0e0a7fb76Kees Cook			(1 << WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE);
92e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek	header->realmode_flags = acpi_realmode_flags;
93e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek	header->real_magic = 0x12345678;
94e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek
95e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek#ifndef CONFIG_64BIT
96e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek	header->pmode_entry = (u32)&wakeup_pmode_return;
97afd51a0e32cd79261f0e823400886ed322a355acAlexander Duyck	header->pmode_cr3 = (u32)__pa_symbol(initial_page_table);
98e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek	saved_magic = 0x12345678;
99e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek#else /* CONFIG_64BIT */
1001ea598c29748a559a0086a84a016886d786e6272Ingo Molnar#ifdef CONFIG_SMP
10111d4c3f9b671720e80353dd7e433ff2bf65e9500H. Peter Anvin	stack_start = (unsigned long)temp_stack + sizeof(temp_stack);
1023038edabf48f01421c621cb77a712b446d3a5d67Rafael J. Wysocki	early_gdt_descr.address =
1033038edabf48f01421c621cb77a712b446d3a5d67Rafael J. Wysocki			(unsigned long)get_cpu_gdt_table(smp_processor_id());
104004aa322f855a765741d9437a98dd8fe2e4f32a6Tejun Heo	initial_gs = per_cpu_offset(smp_processor_id());
1051ea598c29748a559a0086a84a016886d786e6272Ingo Molnar#endif
106e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek	initial_code = (unsigned long)wakeup_long64;
107ce4b3c55475e451cb489e857640396c37ca88974Jaswinder Singh Rajput       saved_magic = 0x123456789abcdef0L;
108e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek#endif /* CONFIG_64BIT */
1094fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek
110f1a2003e22f6b50ea21f7f4b38b38c5ebc9c8017Rafael J. Wysocki	do_suspend_lowlevel();
1114fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek	return 0;
1124fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek}
1134fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek
1144fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machekstatic int __init acpi_sleep_setup(char *str)
1154fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek{
1164fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek	while ((str != NULL) && (*str != '\0')) {
1174fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek		if (strncmp(str, "s3_bios", 7) == 0)
1184fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek			acpi_realmode_flags |= 1;
1194fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek		if (strncmp(str, "s3_mode", 7) == 0)
1204fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek			acpi_realmode_flags |= 2;
1214fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek		if (strncmp(str, "s3_beep", 7) == 0)
1224fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek			acpi_realmode_flags |= 4;
123bdfe6b7c681669148dae4db27eb24ee5408ba371Shaohua Li#ifdef CONFIG_HIBERNATION
124bdfe6b7c681669148dae4db27eb24ee5408ba371Shaohua Li		if (strncmp(str, "s4_nohwsig", 10) == 0)
125bdfe6b7c681669148dae4db27eb24ee5408ba371Shaohua Li			acpi_no_s4_hw_signature();
126bdfe6b7c681669148dae4db27eb24ee5408ba371Shaohua Li#endif
12772ad5d77fb981963edae15eee8196c80238f5ed0Rafael J. Wysocki		if (strncmp(str, "nonvs", 5) == 0)
12872ad5d77fb981963edae15eee8196c80238f5ed0Rafael J. Wysocki			acpi_nvs_nosave();
1291bad2f19f7f79d1ec9e6c48168fd7ce8dc1c305fKristen Carlson Accardi		if (strncmp(str, "nonvs_s3", 8) == 0)
1301bad2f19f7f79d1ec9e6c48168fd7ce8dc1c305fKristen Carlson Accardi			acpi_nvs_nosave_s3();
131d8f3de0d2412bb91639cfefc5b3c79dbf3812212Rafael J. Wysocki		if (strncmp(str, "old_ordering", 12) == 0)
132d8f3de0d2412bb91639cfefc5b3c79dbf3812212Rafael J. Wysocki			acpi_old_suspend_ordering();
1334fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek		str = strchr(str, ',');
1344fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek		if (str != NULL)
1354fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek			str += strspn(str, ", \t");
1364fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek	}
1374fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek	return 1;
1384fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek}
1394fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek
1404fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek__setup("acpi_sleep=", acpi_sleep_setup);
141