sleep.c revision 7a3136666bc0f0419f7aaa7b1fabb4b0e0a7fb76
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>
17b40827fa7268fda8a62490728a61c2856f33830bBorislav Petkov
18e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek#include "realmode/wakeup.h"
19e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek#include "sleep.h"
204fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek
214fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machekunsigned long acpi_realmode_flags;
224fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek
239744f5a32853642f8ed0749a1c9ed8cf9c9c9dc4Marcin Slusarz#if defined(CONFIG_SMP) && defined(CONFIG_64BIT)
245000cadcf3188e935dae28c4fc7e24639704ea55Matt Mackallstatic char temp_stack[4096];
25e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek#endif
264fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek
274fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek/**
28f1a2003e22f6b50ea21f7f4b38b38c5ebc9c8017Rafael J. Wysocki * acpi_suspend_lowlevel - save kernel state
294fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek *
304fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek * Create an identity mapped page table and copy the wakeup routine to
314fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek * low memory.
324fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek */
33f1a2003e22f6b50ea21f7f4b38b38c5ebc9c8017Rafael J. Wysockiint acpi_suspend_lowlevel(void)
344fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek{
35e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek	struct wakeup_header *header;
36d1ee433539ea5963a8f946f3428b335d1c5fdb20H. Peter Anvin	/* address in low memory of the wakeup routine. */
37d1ee433539ea5963a8f946f3428b335d1c5fdb20H. Peter Anvin	char *acpi_realmode;
38e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek
39d1ee433539ea5963a8f946f3428b335d1c5fdb20H. Peter Anvin	acpi_realmode = TRAMPOLINE_SYM(acpi_wakeup_code);
40e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek
41d1ee433539ea5963a8f946f3428b335d1c5fdb20H. Peter Anvin	header = (struct wakeup_header *)(acpi_realmode + WAKEUP_HEADER_OFFSET);
42d1ee433539ea5963a8f946f3428b335d1c5fdb20H. Peter Anvin	if (header->signature != WAKEUP_HEADER_SIGNATURE) {
43e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek		printk(KERN_ERR "wakeup header does not match\n");
44e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek		return -EINVAL;
45e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek	}
46e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek
47e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek	header->video_mode = saved_video_mode;
48e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek
494b4f7280d7fd1feeff134c2cf2db32fd583b6c29H. Peter Anvin	header->wakeup_jmp_seg = acpi_wakeup_address >> 4;
50065cb3dfe24978651caedfa54da585388ad15ddeH. Peter Anvin
51065cb3dfe24978651caedfa54da585388ad15ddeH. Peter Anvin	/*
52065cb3dfe24978651caedfa54da585388ad15ddeH. Peter Anvin	 * Set up the wakeup GDT.  We set these up as Big Real Mode,
53065cb3dfe24978651caedfa54da585388ad15ddeH. Peter Anvin	 * that is, with limits set to 4 GB.  At least the Lenovo
54065cb3dfe24978651caedfa54da585388ad15ddeH. Peter Anvin	 * Thinkpad X61 is known to need this for the video BIOS
55065cb3dfe24978651caedfa54da585388ad15ddeH. Peter Anvin	 * initialization quirk to work; this is likely to also
56065cb3dfe24978651caedfa54da585388ad15ddeH. Peter Anvin	 * be the case for other laptops or integrated video devices.
57065cb3dfe24978651caedfa54da585388ad15ddeH. Peter Anvin	 */
58065cb3dfe24978651caedfa54da585388ad15ddeH. Peter Anvin
594b4f7280d7fd1feeff134c2cf2db32fd583b6c29H. Peter Anvin	/* GDT[0]: GDT self-pointer */
604b4f7280d7fd1feeff134c2cf2db32fd583b6c29H. Peter Anvin	header->wakeup_gdt[0] =
614b4f7280d7fd1feeff134c2cf2db32fd583b6c29H. Peter Anvin		(u64)(sizeof(header->wakeup_gdt) - 1) +
62d1ee433539ea5963a8f946f3428b335d1c5fdb20H. Peter Anvin		((u64)__pa(&header->wakeup_gdt) << 16);
63065cb3dfe24978651caedfa54da585388ad15ddeH. Peter Anvin	/* GDT[1]: big real mode-like code segment */
643bf2e77453a87c22eb57ed4926760ac131c84459H. Peter Anvin	header->wakeup_gdt[1] =
653bf2e77453a87c22eb57ed4926760ac131c84459H. Peter Anvin		GDT_ENTRY(0x809b, acpi_wakeup_address, 0xfffff);
66065cb3dfe24978651caedfa54da585388ad15ddeH. Peter Anvin	/* GDT[2]: big real mode-like data segment */
673bf2e77453a87c22eb57ed4926760ac131c84459H. Peter Anvin	header->wakeup_gdt[2] =
683bf2e77453a87c22eb57ed4926760ac131c84459H. Peter Anvin		GDT_ENTRY(0x8093, acpi_wakeup_address, 0xfffff);
694b4f7280d7fd1feeff134c2cf2db32fd583b6c29H. Peter Anvin
70e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek#ifndef CONFIG_64BIT
71e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek	store_gdt((struct desc_ptr *)&header->pmode_gdt);
72e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek
73a7c4c0d934c6cbc58de262d090d4a715445453f0H. Peter Anvin	if (rdmsr_safe(MSR_EFER, &header->pmode_efer_low,
74a7c4c0d934c6cbc58de262d090d4a715445453f0H. Peter Anvin		       &header->pmode_efer_high))
75a7c4c0d934c6cbc58de262d090d4a715445453f0H. Peter Anvin		header->pmode_efer_low = header->pmode_efer_high = 0;
76e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek#endif /* !CONFIG_64BIT */
77e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek
78e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek	header->pmode_cr0 = read_cr0();
79e532c06f2a835b5cc4f4166f467437d9b09c1d0eDavid Fries	header->pmode_cr4 = read_cr4_safe();
807a3136666bc0f0419f7aaa7b1fabb4b0e0a7fb76Kees Cook	header->pmode_behavior = 0;
817a3136666bc0f0419f7aaa7b1fabb4b0e0a7fb76Kees Cook	if (!rdmsr_safe(MSR_IA32_MISC_ENABLE,
827a3136666bc0f0419f7aaa7b1fabb4b0e0a7fb76Kees Cook			&header->pmode_misc_en_low,
837a3136666bc0f0419f7aaa7b1fabb4b0e0a7fb76Kees Cook			&header->pmode_misc_en_high))
847a3136666bc0f0419f7aaa7b1fabb4b0e0a7fb76Kees Cook		header->pmode_behavior |=
857a3136666bc0f0419f7aaa7b1fabb4b0e0a7fb76Kees Cook			(1 << WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE);
86e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek	header->realmode_flags = acpi_realmode_flags;
87e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek	header->real_magic = 0x12345678;
88e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek
89e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek#ifndef CONFIG_64BIT
90e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek	header->pmode_entry = (u32)&wakeup_pmode_return;
91b40827fa7268fda8a62490728a61c2856f33830bBorislav Petkov	header->pmode_cr3 = (u32)__pa(&initial_page_table);
92e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek	saved_magic = 0x12345678;
93e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek#else /* CONFIG_64BIT */
94d1ee433539ea5963a8f946f3428b335d1c5fdb20H. Peter Anvin	header->trampoline_segment = trampoline_address() >> 4;
951ea598c29748a559a0086a84a016886d786e6272Ingo Molnar#ifdef CONFIG_SMP
9611d4c3f9b671720e80353dd7e433ff2bf65e9500H. Peter Anvin	stack_start = (unsigned long)temp_stack + sizeof(temp_stack);
973038edabf48f01421c621cb77a712b446d3a5d67Rafael J. Wysocki	early_gdt_descr.address =
983038edabf48f01421c621cb77a712b446d3a5d67Rafael J. Wysocki			(unsigned long)get_cpu_gdt_table(smp_processor_id());
99004aa322f855a765741d9437a98dd8fe2e4f32a6Tejun Heo	initial_gs = per_cpu_offset(smp_processor_id());
1001ea598c29748a559a0086a84a016886d786e6272Ingo Molnar#endif
101e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek	initial_code = (unsigned long)wakeup_long64;
102ce4b3c55475e451cb489e857640396c37ca88974Jaswinder Singh Rajput       saved_magic = 0x123456789abcdef0L;
103e44b7b7525ad9d43163ab5e60c784325419e0ea6Pavel Machek#endif /* CONFIG_64BIT */
1044fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek
105f1a2003e22f6b50ea21f7f4b38b38c5ebc9c8017Rafael J. Wysocki	do_suspend_lowlevel();
1064fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek	return 0;
1074fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek}
1084fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek
1094fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machekstatic int __init acpi_sleep_setup(char *str)
1104fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek{
1114fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek	while ((str != NULL) && (*str != '\0')) {
1124fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek		if (strncmp(str, "s3_bios", 7) == 0)
1134fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek			acpi_realmode_flags |= 1;
1144fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek		if (strncmp(str, "s3_mode", 7) == 0)
1154fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek			acpi_realmode_flags |= 2;
1164fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek		if (strncmp(str, "s3_beep", 7) == 0)
1174fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek			acpi_realmode_flags |= 4;
118bdfe6b7c681669148dae4db27eb24ee5408ba371Shaohua Li#ifdef CONFIG_HIBERNATION
119bdfe6b7c681669148dae4db27eb24ee5408ba371Shaohua Li		if (strncmp(str, "s4_nohwsig", 10) == 0)
120bdfe6b7c681669148dae4db27eb24ee5408ba371Shaohua Li			acpi_no_s4_hw_signature();
121bdfe6b7c681669148dae4db27eb24ee5408ba371Shaohua Li#endif
12272ad5d77fb981963edae15eee8196c80238f5ed0Rafael J. Wysocki		if (strncmp(str, "nonvs", 5) == 0)
12372ad5d77fb981963edae15eee8196c80238f5ed0Rafael J. Wysocki			acpi_nvs_nosave();
124d8f3de0d2412bb91639cfefc5b3c79dbf3812212Rafael J. Wysocki		if (strncmp(str, "old_ordering", 12) == 0)
125d8f3de0d2412bb91639cfefc5b3c79dbf3812212Rafael J. Wysocki			acpi_old_suspend_ordering();
1264fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek		str = strchr(str, ',');
1274fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek		if (str != NULL)
1284fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek			str += strspn(str, ", \t");
1294fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek	}
1304fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek	return 1;
1314fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek}
1324fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek
1334fc2fba804cae404d2665e23b8cbd46d5f63a07ePavel Machek__setup("acpi_sleep=", acpi_sleep_setup);
134