aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/acpi
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/kernel/acpi')
-rw-r--r--arch/x86/kernel/acpi/Makefile9
-rw-r--r--arch/x86/kernel/acpi/realmode/Makefile57
-rw-r--r--arch/x86/kernel/acpi/realmode/copy.S1
-rw-r--r--arch/x86/kernel/acpi/realmode/video-bios.c1
-rw-r--r--arch/x86/kernel/acpi/realmode/video-mode.c1
-rw-r--r--arch/x86/kernel/acpi/realmode/video-vesa.c1
-rw-r--r--arch/x86/kernel/acpi/realmode/video-vga.c1
-rw-r--r--arch/x86/kernel/acpi/realmode/wakemain.c81
-rw-r--r--arch/x86/kernel/acpi/realmode/wakeup.S113
-rw-r--r--arch/x86/kernel/acpi/realmode/wakeup.h36
-rw-r--r--arch/x86/kernel/acpi/realmode/wakeup.lds.S61
-rw-r--r--arch/x86/kernel/acpi/sleep.c71
-rw-r--r--arch/x86/kernel/acpi/sleep.h16
-rw-r--r--arch/x86/kernel/acpi/sleep_32.c40
-rw-r--r--arch/x86/kernel/acpi/wakeup_32.S247
-rw-r--r--arch/x86/kernel/acpi/wakeup_64.S313
-rw-r--r--arch/x86/kernel/acpi/wakeup_rm.S10
17 files changed, 475 insertions, 584 deletions
diff --git a/arch/x86/kernel/acpi/Makefile b/arch/x86/kernel/acpi/Makefile
index 19d3d6e9d09b..7335959b6aff 100644
--- a/arch/x86/kernel/acpi/Makefile
+++ b/arch/x86/kernel/acpi/Makefile
@@ -1,7 +1,14 @@
1subdir- := realmode
2
1obj-$(CONFIG_ACPI) += boot.o 3obj-$(CONFIG_ACPI) += boot.o
2obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o 4obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_rm.o wakeup_$(BITS).o
3 5
4ifneq ($(CONFIG_ACPI_PROCESSOR),) 6ifneq ($(CONFIG_ACPI_PROCESSOR),)
5obj-y += cstate.o processor.o 7obj-y += cstate.o processor.o
6endif 8endif
7 9
10$(obj)/wakeup_rm.o: $(obj)/realmode/wakeup.bin
11
12$(obj)/realmode/wakeup.bin: FORCE
13 $(Q)$(MAKE) $(build)=$(obj)/realmode $@
14
diff --git a/arch/x86/kernel/acpi/realmode/Makefile b/arch/x86/kernel/acpi/realmode/Makefile
new file mode 100644
index 000000000000..092900854acc
--- /dev/null
+++ b/arch/x86/kernel/acpi/realmode/Makefile
@@ -0,0 +1,57 @@
1#
2# arch/x86/kernel/acpi/realmode/Makefile
3#
4# This file is subject to the terms and conditions of the GNU General Public
5# License. See the file "COPYING" in the main directory of this archive
6# for more details.
7#
8
9targets := wakeup.bin wakeup.elf
10
11wakeup-y += wakeup.o wakemain.o video-mode.o copy.o
12
13# The link order of the video-*.o modules can matter. In particular,
14# video-vga.o *must* be listed first, followed by video-vesa.o.
15# Hardware-specific drivers should follow in the order they should be
16# probed, and video-bios.o should typically be last.
17wakeup-y += video-vga.o
18wakeup-y += video-vesa.o
19wakeup-y += video-bios.o
20
21targets += $(wakeup-y)
22
23bootsrc := $(src)/../../../boot
24
25# ---------------------------------------------------------------------------
26
27# How to compile the 16-bit code. Note we always compile for -march=i386,
28# that way we can complain to the user if the CPU is insufficient.
29# Compile with _SETUP since this is similar to the boot-time setup code.
30KBUILD_CFLAGS := $(LINUXINCLUDE) -g -Os -D_SETUP -D_WAKEUP -D__KERNEL__ \
31 -I$(srctree)/$(bootsrc) \
32 $(cflags-y) \
33 -Wall -Wstrict-prototypes \
34 -march=i386 -mregparm=3 \
35 -include $(srctree)/$(bootsrc)/code16gcc.h \
36 -fno-strict-aliasing -fomit-frame-pointer \
37 $(call cc-option, -ffreestanding) \
38 $(call cc-option, -fno-toplevel-reorder,\
39 $(call cc-option, -fno-unit-at-a-time)) \
40 $(call cc-option, -fno-stack-protector) \
41 $(call cc-option, -mpreferred-stack-boundary=2)
42KBUILD_CFLAGS += $(call cc-option, -m32)
43KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__
44
45WAKEUP_OBJS = $(addprefix $(obj)/,$(wakeup-y))
46
47LDFLAGS_wakeup.elf := -T
48
49CPPFLAGS_wakeup.lds += -P -C
50
51$(obj)/wakeup.elf: $(src)/wakeup.lds $(WAKEUP_OBJS) FORCE
52 $(call if_changed,ld)
53
54OBJCOPYFLAGS_wakeup.bin := -O binary
55
56$(obj)/wakeup.bin: $(obj)/wakeup.elf FORCE
57 $(call if_changed,objcopy)
diff --git a/arch/x86/kernel/acpi/realmode/copy.S b/arch/x86/kernel/acpi/realmode/copy.S
new file mode 100644
index 000000000000..dc59ebee69d8
--- /dev/null
+++ b/arch/x86/kernel/acpi/realmode/copy.S
@@ -0,0 +1 @@
#include "../../../boot/copy.S"
diff --git a/arch/x86/kernel/acpi/realmode/video-bios.c b/arch/x86/kernel/acpi/realmode/video-bios.c
new file mode 100644
index 000000000000..7deabc144a27
--- /dev/null
+++ b/arch/x86/kernel/acpi/realmode/video-bios.c
@@ -0,0 +1 @@
#include "../../../boot/video-bios.c"
diff --git a/arch/x86/kernel/acpi/realmode/video-mode.c b/arch/x86/kernel/acpi/realmode/video-mode.c
new file mode 100644
index 000000000000..328ad209f113
--- /dev/null
+++ b/arch/x86/kernel/acpi/realmode/video-mode.c
@@ -0,0 +1 @@
#include "../../../boot/video-mode.c"
diff --git a/arch/x86/kernel/acpi/realmode/video-vesa.c b/arch/x86/kernel/acpi/realmode/video-vesa.c
new file mode 100644
index 000000000000..9dbb9672226a
--- /dev/null
+++ b/arch/x86/kernel/acpi/realmode/video-vesa.c
@@ -0,0 +1 @@
#include "../../../boot/video-vesa.c"
diff --git a/arch/x86/kernel/acpi/realmode/video-vga.c b/arch/x86/kernel/acpi/realmode/video-vga.c
new file mode 100644
index 000000000000..bcc81255f374
--- /dev/null
+++ b/arch/x86/kernel/acpi/realmode/video-vga.c
@@ -0,0 +1 @@
#include "../../../boot/video-vga.c"
diff --git a/arch/x86/kernel/acpi/realmode/wakemain.c b/arch/x86/kernel/acpi/realmode/wakemain.c
new file mode 100644
index 000000000000..883962d9eef2
--- /dev/null
+++ b/arch/x86/kernel/acpi/realmode/wakemain.c
@@ -0,0 +1,81 @@
1#include "wakeup.h"
2#include "boot.h"
3
4static void udelay(int loops)
5{
6 while (loops--)
7 io_delay(); /* Approximately 1 us */
8}
9
10static void beep(unsigned int hz)
11{
12 u8 enable;
13
14 if (!hz) {
15 enable = 0x00; /* Turn off speaker */
16 } else {
17 u16 div = 1193181/hz;
18
19 outb(0xb6, 0x43); /* Ctr 2, squarewave, load, binary */
20 io_delay();
21 outb(div, 0x42); /* LSB of counter */
22 io_delay();
23 outb(div >> 8, 0x42); /* MSB of counter */
24 io_delay();
25
26 enable = 0x03; /* Turn on speaker */
27 }
28 inb(0x61); /* Dummy read of System Control Port B */
29 io_delay();
30 outb(enable, 0x61); /* Enable timer 2 output to speaker */
31 io_delay();
32}
33
34#define DOT_HZ 880
35#define DASH_HZ 587
36#define US_PER_DOT 125000
37
38/* Okay, this is totally silly, but it's kind of fun. */
39static void send_morse(const char *pattern)
40{
41 char s;
42
43 while ((s = *pattern++)) {
44 switch (s) {
45 case '.':
46 beep(DOT_HZ);
47 udelay(US_PER_DOT);
48 beep(0);
49 udelay(US_PER_DOT);
50 break;
51 case '-':
52 beep(DASH_HZ);
53 udelay(US_PER_DOT * 3);
54 beep(0);
55 udelay(US_PER_DOT);
56 break;
57 default: /* Assume it's a space */
58 udelay(US_PER_DOT * 3);
59 break;
60 }
61 }
62}
63
64void main(void)
65{
66 /* Kill machine if structures are wrong */
67 if (wakeup_header.real_magic != 0x12345678)
68 while (1);
69
70 if (wakeup_header.realmode_flags & 4)
71 send_morse("...-");
72
73 if (wakeup_header.realmode_flags & 1)
74 asm volatile("lcallw $0xc000,$3");
75
76 if (wakeup_header.realmode_flags & 2) {
77 /* Need to call BIOS */
78 probe_cards(0);
79 set_mode(wakeup_header.video_mode);
80 }
81}
diff --git a/arch/x86/kernel/acpi/realmode/wakeup.S b/arch/x86/kernel/acpi/realmode/wakeup.S
new file mode 100644
index 000000000000..f9b77fb37e5b
--- /dev/null
+++ b/arch/x86/kernel/acpi/realmode/wakeup.S
@@ -0,0 +1,113 @@
1/*
2 * ACPI wakeup real mode startup stub
3 */
4#include <asm/segment.h>
5#include <asm/msr-index.h>
6#include <asm/page.h>
7#include <asm/pgtable.h>
8
9 .code16
10 .section ".header", "a"
11
12/* This should match the structure in wakeup.h */
13 .globl wakeup_header
14wakeup_header:
15video_mode: .short 0 /* Video mode number */
16pmode_return: .byte 0x66, 0xea /* ljmpl */
17 .long 0 /* offset goes here */
18 .short __KERNEL_CS
19pmode_cr0: .long 0 /* Saved %cr0 */
20pmode_cr3: .long 0 /* Saved %cr3 */
21pmode_cr4: .long 0 /* Saved %cr4 */
22pmode_efer: .quad 0 /* Saved EFER */
23pmode_gdt: .quad 0
24realmode_flags: .long 0
25real_magic: .long 0
26trampoline_segment: .word 0
27signature: .long 0x51ee1111
28
29 .text
30 .globl _start
31 .code16
32wakeup_code:
33_start:
34 cli
35 cld
36
37 /* Set up segments */
38 movw %cs, %ax
39 movw %ax, %ds
40 movw %ax, %es
41 movw %ax, %ss
42
43 movl $wakeup_stack_end, %esp
44
45 /* Clear the EFLAGS */
46 pushl $0
47 popfl
48
49 /* Check header signature... */
50 movl signature, %eax
51 cmpl $0x51ee1111, %eax
52 jne bogus_real_magic
53
54 /* Check we really have everything... */
55 movl end_signature, %eax
56 cmpl $0x65a22c82, %eax
57 jne bogus_real_magic
58
59 /* Call the C code */
60 calll main
61
62 /* Do any other stuff... */
63
64#ifndef CONFIG_64BIT
65 /* This could also be done in C code... */
66 movl pmode_cr3, %eax
67 movl %eax, %cr3
68
69 movl pmode_cr4, %ecx
70 jecxz 1f
71 movl %ecx, %cr4
721:
73 movl pmode_efer, %eax
74 movl pmode_efer + 4, %edx
75 movl %eax, %ecx
76 orl %edx, %ecx
77 jz 1f
78 movl $0xc0000080, %ecx
79 wrmsr
801:
81
82 lgdtl pmode_gdt
83
84 /* This really couldn't... */
85 movl pmode_cr0, %eax
86 movl %eax, %cr0
87 jmp pmode_return
88#else
89 pushw $0
90 pushw trampoline_segment
91 pushw $0
92 lret
93#endif
94
95bogus_real_magic:
961:
97 hlt
98 jmp 1b
99
100 .data
101 .balign 4
102 .globl HEAP, heap_end
103HEAP:
104 .long wakeup_heap
105heap_end:
106 .long wakeup_stack
107
108 .bss
109wakeup_heap:
110 .space 2048
111wakeup_stack:
112 .space 2048
113wakeup_stack_end:
diff --git a/arch/x86/kernel/acpi/realmode/wakeup.h b/arch/x86/kernel/acpi/realmode/wakeup.h
new file mode 100644
index 000000000000..ef8166fe8020
--- /dev/null
+++ b/arch/x86/kernel/acpi/realmode/wakeup.h
@@ -0,0 +1,36 @@
1/*
2 * Definitions for the wakeup data structure at the head of the
3 * wakeup code.
4 */
5
6#ifndef ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H
7#define ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H
8
9#ifndef __ASSEMBLY__
10#include <linux/types.h>
11
12/* This must match data at wakeup.S */
13struct wakeup_header {
14 u16 video_mode; /* Video mode number */
15 u16 _jmp1; /* ljmpl opcode, 32-bit only */
16 u32 pmode_entry; /* Protected mode resume point, 32-bit only */
17 u16 _jmp2; /* CS value, 32-bit only */
18 u32 pmode_cr0; /* Protected mode cr0 */
19 u32 pmode_cr3; /* Protected mode cr3 */
20 u32 pmode_cr4; /* Protected mode cr4 */
21 u32 pmode_efer_low; /* Protected mode EFER */
22 u32 pmode_efer_high;
23 u64 pmode_gdt;
24 u32 realmode_flags;
25 u32 real_magic;
26 u16 trampoline_segment; /* segment with trampoline code, 64-bit only */
27 u32 signature; /* To check we have correct structure */
28} __attribute__((__packed__));
29
30extern struct wakeup_header wakeup_header;
31#endif
32
33#define HEADER_OFFSET 0x3f00
34#define WAKEUP_SIZE 0x4000
35
36#endif /* ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H */
diff --git a/arch/x86/kernel/acpi/realmode/wakeup.lds.S b/arch/x86/kernel/acpi/realmode/wakeup.lds.S
new file mode 100644
index 000000000000..22fab6c4be15
--- /dev/null
+++ b/arch/x86/kernel/acpi/realmode/wakeup.lds.S
@@ -0,0 +1,61 @@
1/*
2 * wakeup.ld
3 *
4 * Linker script for the real-mode wakeup code
5 */
6#undef i386
7#include "wakeup.h"
8
9OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
10OUTPUT_ARCH(i386)
11ENTRY(_start)
12
13SECTIONS
14{
15 . = HEADER_OFFSET;
16 .header : {
17 *(.header)
18 }
19
20 . = 0;
21 .text : {
22 *(.text*)
23 }
24
25 . = ALIGN(16);
26 .rodata : {
27 *(.rodata*)
28 }
29
30 .videocards : {
31 video_cards = .;
32 *(.videocards)
33 video_cards_end = .;
34 }
35
36 . = ALIGN(16);
37 .data : {
38 *(.data*)
39 }
40
41 .signature : {
42 end_signature = .;
43 LONG(0x65a22c82)
44 }
45
46 . = ALIGN(16);
47 .bss : {
48 __bss_start = .;
49 *(.bss)
50 __bss_end = .;
51 }
52
53 . = ALIGN(16);
54 _end = .;
55
56 /DISCARD/ : {
57 *(.note*)
58 }
59
60 . = ASSERT(_end <= WAKEUP_SIZE, "Wakeup too big!");
61}
diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c
index dd78326ae47c..afc25ee9964b 100644
--- a/arch/x86/kernel/acpi/sleep.c
+++ b/arch/x86/kernel/acpi/sleep.c
@@ -10,30 +10,72 @@
10#include <linux/dmi.h> 10#include <linux/dmi.h>
11#include <linux/cpumask.h> 11#include <linux/cpumask.h>
12 12
13#include <asm/smp.h> 13#include "realmode/wakeup.h"
14#include "sleep.h"
14 15
15/* address in low memory of the wakeup routine. */
16unsigned long acpi_wakeup_address; 16unsigned long acpi_wakeup_address;
17unsigned long acpi_realmode_flags; 17unsigned long acpi_realmode_flags;
18extern char wakeup_start, wakeup_end;
19 18
20extern unsigned long acpi_copy_wakeup_routine(unsigned long); 19/* address in low memory of the wakeup routine. */
20static unsigned long acpi_realmode;
21
22#ifdef CONFIG_64BIT
23static char temp_stack[10240];
24#endif
21 25
22/** 26/**
23 * acpi_save_state_mem - save kernel state 27 * acpi_save_state_mem - save kernel state
24 * 28 *
25 * Create an identity mapped page table and copy the wakeup routine to 29 * Create an identity mapped page table and copy the wakeup routine to
26 * low memory. 30 * low memory.
31 *
32 * Note that this is too late to change acpi_wakeup_address.
27 */ 33 */
28int acpi_save_state_mem(void) 34int acpi_save_state_mem(void)
29{ 35{
30 if (!acpi_wakeup_address) { 36 struct wakeup_header *header;
31 printk(KERN_ERR "Could not allocate memory during boot, S3 disabled\n"); 37
38 if (!acpi_realmode) {
39 printk(KERN_ERR "Could not allocate memory during boot, "
40 "S3 disabled\n");
32 return -ENOMEM; 41 return -ENOMEM;
33 } 42 }
34 memcpy((void *)acpi_wakeup_address, &wakeup_start, 43 memcpy((void *)acpi_realmode, &wakeup_code_start, WAKEUP_SIZE);
35 &wakeup_end - &wakeup_start); 44
36 acpi_copy_wakeup_routine(acpi_wakeup_address); 45 header = (struct wakeup_header *)(acpi_realmode + HEADER_OFFSET);
46 if (header->signature != 0x51ee1111) {
47 printk(KERN_ERR "wakeup header does not match\n");
48 return -EINVAL;
49 }
50
51 header->video_mode = saved_video_mode;
52
53#ifndef CONFIG_64BIT
54 store_gdt((struct desc_ptr *)&header->pmode_gdt);
55
56 header->pmode_efer_low = nx_enabled;
57 if (header->pmode_efer_low & 1) {
58 /* This is strange, why not save efer, always? */
59 rdmsr(MSR_EFER, header->pmode_efer_low,
60 header->pmode_efer_high);
61 }
62#endif /* !CONFIG_64BIT */
63
64 header->pmode_cr0 = read_cr0();
65 header->pmode_cr4 = read_cr4();
66 header->realmode_flags = acpi_realmode_flags;
67 header->real_magic = 0x12345678;
68
69#ifndef CONFIG_64BIT
70 header->pmode_entry = (u32)&wakeup_pmode_return;
71 header->pmode_cr3 = (u32)(swsusp_pg_dir - __PAGE_OFFSET);
72 saved_magic = 0x12345678;
73#else /* CONFIG_64BIT */
74 header->trampoline_segment = setup_trampoline() >> 4;
75 init_rsp = (unsigned long)temp_stack + 4096;
76 initial_code = (unsigned long)wakeup_long64;
77 saved_magic = 0x123456789abcdef0;
78#endif /* CONFIG_64BIT */
37 79
38 return 0; 80 return 0;
39} 81}
@@ -56,15 +98,20 @@ void acpi_restore_state_mem(void)
56 */ 98 */
57void __init acpi_reserve_bootmem(void) 99void __init acpi_reserve_bootmem(void)
58{ 100{
59 if ((&wakeup_end - &wakeup_start) > PAGE_SIZE*2) { 101 if ((&wakeup_code_end - &wakeup_code_start) > WAKEUP_SIZE) {
60 printk(KERN_ERR 102 printk(KERN_ERR
61 "ACPI: Wakeup code way too big, S3 disabled.\n"); 103 "ACPI: Wakeup code way too big, S3 disabled.\n");
62 return; 104 return;
63 } 105 }
64 106
65 acpi_wakeup_address = (unsigned long)alloc_bootmem_low(PAGE_SIZE*2); 107 acpi_realmode = (unsigned long)alloc_bootmem_low(WAKEUP_SIZE);
66 if (!acpi_wakeup_address) 108
109 if (!acpi_realmode) {
67 printk(KERN_ERR "ACPI: Cannot allocate lowmem, S3 disabled.\n"); 110 printk(KERN_ERR "ACPI: Cannot allocate lowmem, S3 disabled.\n");
111 return;
112 }
113
114 acpi_wakeup_address = acpi_realmode;
68} 115}
69 116
70 117
diff --git a/arch/x86/kernel/acpi/sleep.h b/arch/x86/kernel/acpi/sleep.h
new file mode 100644
index 000000000000..adbcbaa6f1df
--- /dev/null
+++ b/arch/x86/kernel/acpi/sleep.h
@@ -0,0 +1,16 @@
1/*
2 * Variables and functions used by the code in sleep.c
3 */
4
5#include <asm/trampoline.h>
6
7extern char wakeup_code_start, wakeup_code_end;
8
9extern unsigned long saved_video_mode;
10extern long saved_magic;
11
12extern int wakeup_pmode_return;
13extern char swsusp_pg_dir[PAGE_SIZE];
14
15extern unsigned long acpi_copy_wakeup_routine(unsigned long);
16extern void wakeup_long64(void);
diff --git a/arch/x86/kernel/acpi/sleep_32.c b/arch/x86/kernel/acpi/sleep_32.c
deleted file mode 100644
index 63fe5525e026..000000000000
--- a/arch/x86/kernel/acpi/sleep_32.c
+++ /dev/null
@@ -1,40 +0,0 @@
1/*
2 * sleep.c - x86-specific ACPI sleep support.
3 *
4 * Copyright (C) 2001-2003 Patrick Mochel
5 * Copyright (C) 2001-2003 Pavel Machek <pavel@suse.cz>
6 */
7
8#include <linux/acpi.h>
9#include <linux/bootmem.h>
10#include <linux/dmi.h>
11#include <linux/cpumask.h>
12
13#include <asm/smp.h>
14
15/* Ouch, we want to delete this. We already have better version in userspace, in
16 s2ram from suspend.sf.net project */
17static __init int reset_videomode_after_s3(const struct dmi_system_id *d)
18{
19 acpi_realmode_flags |= 2;
20 return 0;
21}
22
23static __initdata struct dmi_system_id acpisleep_dmi_table[] = {
24 { /* Reset video mode after returning from ACPI S3 sleep */
25 .callback = reset_videomode_after_s3,
26 .ident = "Toshiba Satellite 4030cdt",
27 .matches = {
28 DMI_MATCH(DMI_PRODUCT_NAME, "S4030CDT/4.3"),
29 },
30 },
31 {}
32};
33
34static int __init acpisleep_dmi_init(void)
35{
36 dmi_check_system(acpisleep_dmi_table);
37 return 0;
38}
39
40core_initcall(acpisleep_dmi_init);
diff --git a/arch/x86/kernel/acpi/wakeup_32.S b/arch/x86/kernel/acpi/wakeup_32.S
index f53e3277f8e5..a12e6a9fb659 100644
--- a/arch/x86/kernel/acpi/wakeup_32.S
+++ b/arch/x86/kernel/acpi/wakeup_32.S
@@ -3,178 +3,12 @@
3#include <asm/segment.h> 3#include <asm/segment.h>
4#include <asm/page.h> 4#include <asm/page.h>
5 5
6# 6# Copyright 2003, 2008 Pavel Machek <pavel@suse.cz>, distribute under GPLv2
7# wakeup_code runs in real mode, and at unknown address (determined at run-time).
8# Therefore it must only use relative jumps/calls.
9#
10# Do we need to deal with A20? It is okay: ACPI specs says A20 must be enabled
11#
12# If physical address of wakeup_code is 0x12345, BIOS should call us with
13# cs = 0x1234, eip = 0x05
14#
15
16#define BEEP \
17 inb $97, %al; \
18 outb %al, $0x80; \
19 movb $3, %al; \
20 outb %al, $97; \
21 outb %al, $0x80; \
22 movb $-74, %al; \
23 outb %al, $67; \
24 outb %al, $0x80; \
25 movb $-119, %al; \
26 outb %al, $66; \
27 outb %al, $0x80; \
28 movb $15, %al; \
29 outb %al, $66;
30
31ALIGN
32 .align 4096
33ENTRY(wakeup_start)
34wakeup_code:
35 wakeup_code_start = .
36 .code16
37
38 cli
39 cld
40
41 # setup data segment
42 movw %cs, %ax
43 movw %ax, %ds # Make ds:0 point to wakeup_start
44 movw %ax, %ss
45
46 testl $4, realmode_flags - wakeup_code
47 jz 1f
48 BEEP
491:
50 mov $(wakeup_stack - wakeup_code), %sp # Private stack is needed for ASUS board
51
52 pushl $0 # Kill any dangerous flags
53 popfl
54
55 movl real_magic - wakeup_code, %eax
56 cmpl $0x12345678, %eax
57 jne bogus_real_magic
58
59 testl $1, realmode_flags - wakeup_code
60 jz 1f
61 lcall $0xc000,$3
62 movw %cs, %ax
63 movw %ax, %ds # Bios might have played with that
64 movw %ax, %ss
651:
66
67 testl $2, realmode_flags - wakeup_code
68 jz 1f
69 mov video_mode - wakeup_code, %ax
70 call mode_set
711:
72
73 # set up page table
74 movl $swsusp_pg_dir-__PAGE_OFFSET, %eax
75 movl %eax, %cr3
76
77 testl $1, real_efer_save_restore - wakeup_code
78 jz 4f
79 # restore efer setting
80 movl real_save_efer_edx - wakeup_code, %edx
81 movl real_save_efer_eax - wakeup_code, %eax
82 mov $0xc0000080, %ecx
83 wrmsr
844:
85 # make sure %cr4 is set correctly (features, etc)
86 movl real_save_cr4 - wakeup_code, %eax
87 movl %eax, %cr4
88
89 # need a gdt -- use lgdtl to force 32-bit operands, in case
90 # the GDT is located past 16 megabytes.
91 lgdtl real_save_gdt - wakeup_code
92
93 movl real_save_cr0 - wakeup_code, %eax
94 movl %eax, %cr0
95 jmp 1f
961:
97 movl real_magic - wakeup_code, %eax
98 cmpl $0x12345678, %eax
99 jne bogus_real_magic
100
101 testl $8, realmode_flags - wakeup_code
102 jz 1f
103 BEEP
1041:
105 ljmpl $__KERNEL_CS, $wakeup_pmode_return
106
107real_save_gdt: .word 0
108 .long 0
109real_save_cr0: .long 0
110real_save_cr3: .long 0
111real_save_cr4: .long 0
112real_magic: .long 0
113video_mode: .long 0
114realmode_flags: .long 0
115real_efer_save_restore: .long 0
116real_save_efer_edx: .long 0
117real_save_efer_eax: .long 0
118
119bogus_real_magic:
120 jmp bogus_real_magic
121
122/* This code uses an extended set of video mode numbers. These include:
123 * Aliases for standard modes
124 * NORMAL_VGA (-1)
125 * EXTENDED_VGA (-2)
126 * ASK_VGA (-3)
127 * Video modes numbered by menu position -- NOT RECOMMENDED because of lack
128 * of compatibility when extending the table. These are between 0x00 and 0xff.
129 */
130#define VIDEO_FIRST_MENU 0x0000
131
132/* Standard BIOS video modes (BIOS number + 0x0100) */
133#define VIDEO_FIRST_BIOS 0x0100
134
135/* VESA BIOS video modes (VESA number + 0x0200) */
136#define VIDEO_FIRST_VESA 0x0200
137
138/* Video7 special modes (BIOS number + 0x0900) */
139#define VIDEO_FIRST_V7 0x0900
140
141# Setting of user mode (AX=mode ID) => CF=success
142
143# For now, we only handle VESA modes (0x0200..0x03ff). To handle other
144# modes, we should probably compile in the video code from the boot
145# directory.
146mode_set:
147 movw %ax, %bx
148 subb $VIDEO_FIRST_VESA>>8, %bh
149 cmpb $2, %bh
150 jb check_vesa
151
152setbad:
153 clc
154 ret
155
156check_vesa:
157 orw $0x4000, %bx # Use linear frame buffer
158 movw $0x4f02, %ax # VESA BIOS mode set call
159 int $0x10
160 cmpw $0x004f, %ax # AL=4f if implemented
161 jnz setbad # AH=0 if OK
162
163 stc
164 ret
165 7
166 .code32 8 .code32
167 ALIGN 9 ALIGN
168 10
169.org 0x800 11ENTRY(wakeup_pmode_return)
170wakeup_stack_begin: # Stack grows down
171
172.org 0xff0 # Just below end of page
173wakeup_stack:
174ENTRY(wakeup_end)
175
176.org 0x1000
177
178wakeup_pmode_return: 12wakeup_pmode_return:
179 movw $__KERNEL_DS, %ax 13 movw $__KERNEL_DS, %ax
180 movw %ax, %ss 14 movw %ax, %ss
@@ -187,7 +21,7 @@ wakeup_pmode_return:
187 lgdt saved_gdt 21 lgdt saved_gdt
188 lidt saved_idt 22 lidt saved_idt
189 lldt saved_ldt 23 lldt saved_ldt
190 ljmp $(__KERNEL_CS),$1f 24 ljmp $(__KERNEL_CS), $1f
1911: 251:
192 movl %cr3, %eax 26 movl %cr3, %eax
193 movl %eax, %cr3 27 movl %eax, %cr3
@@ -201,82 +35,41 @@ wakeup_pmode_return:
201 jne bogus_magic 35 jne bogus_magic
202 36
203 # jump to place where we left off 37 # jump to place where we left off
204 movl saved_eip,%eax 38 movl saved_eip, %eax
205 jmp *%eax 39 jmp *%eax
206 40
207bogus_magic: 41bogus_magic:
208 jmp bogus_magic 42 jmp bogus_magic
209 43
210 44
211##
212# acpi_copy_wakeup_routine
213#
214# Copy the above routine to low memory.
215#
216# Parameters:
217# %eax: place to copy wakeup routine to
218#
219# Returned address is location of code in low memory (past data and stack)
220#
221ENTRY(acpi_copy_wakeup_routine)
222 45
223 pushl %ebx 46save_registers:
224 sgdt saved_gdt 47 sgdt saved_gdt
225 sidt saved_idt 48 sidt saved_idt
226 sldt saved_ldt 49 sldt saved_ldt
227 str saved_tss 50 str saved_tss
228 51
229 movl nx_enabled, %edx
230 movl %edx, real_efer_save_restore - wakeup_start (%eax)
231 testl $1, real_efer_save_restore - wakeup_start (%eax)
232 jz 2f
233 # save efer setting
234 pushl %eax
235 movl %eax, %ebx
236 mov $0xc0000080, %ecx
237 rdmsr
238 movl %edx, real_save_efer_edx - wakeup_start (%ebx)
239 movl %eax, real_save_efer_eax - wakeup_start (%ebx)
240 popl %eax
2412:
242
243 movl %cr3, %edx
244 movl %edx, real_save_cr3 - wakeup_start (%eax)
245 movl %cr4, %edx
246 movl %edx, real_save_cr4 - wakeup_start (%eax)
247 movl %cr0, %edx
248 movl %edx, real_save_cr0 - wakeup_start (%eax)
249 sgdt real_save_gdt - wakeup_start (%eax)
250
251 movl saved_videomode, %edx
252 movl %edx, video_mode - wakeup_start (%eax)
253 movl acpi_realmode_flags, %edx
254 movl %edx, realmode_flags - wakeup_start (%eax)
255 movl $0x12345678, real_magic - wakeup_start (%eax)
256 movl $0x12345678, saved_magic
257 popl %ebx
258 ret
259
260save_registers:
261 leal 4(%esp), %eax 52 leal 4(%esp), %eax
262 movl %eax, saved_context_esp 53 movl %eax, saved_context_esp
263 movl %ebx, saved_context_ebx 54 movl %ebx, saved_context_ebx
264 movl %ebp, saved_context_ebp 55 movl %ebp, saved_context_ebp
265 movl %esi, saved_context_esi 56 movl %esi, saved_context_esi
266 movl %edi, saved_context_edi 57 movl %edi, saved_context_edi
267 pushfl ; popl saved_context_eflags 58 pushfl
268 59 popl saved_context_eflags
269 movl $ret_point, saved_eip 60
61 movl $ret_point, saved_eip
270 ret 62 ret
271 63
272 64
273restore_registers: 65restore_registers:
274 movl saved_context_ebp, %ebp 66 movl saved_context_ebp, %ebp
275 movl saved_context_ebx, %ebx 67 movl saved_context_ebx, %ebx
276 movl saved_context_esi, %esi 68 movl saved_context_esi, %esi
277 movl saved_context_edi, %edi 69 movl saved_context_edi, %edi
278 pushl saved_context_eflags ; popfl 70 pushl saved_context_eflags
279 ret 71 popfl
72 ret
280 73
281ENTRY(do_suspend_lowlevel) 74ENTRY(do_suspend_lowlevel)
282 call save_processor_state 75 call save_processor_state
diff --git a/arch/x86/kernel/acpi/wakeup_64.S b/arch/x86/kernel/acpi/wakeup_64.S
index 2e1b9e0d0767..bcc293423a70 100644
--- a/arch/x86/kernel/acpi/wakeup_64.S
+++ b/arch/x86/kernel/acpi/wakeup_64.S
@@ -7,191 +7,18 @@
7#include <asm/asm-offsets.h> 7#include <asm/asm-offsets.h>
8 8
9# Copyright 2003 Pavel Machek <pavel@suse.cz>, distribute under GPLv2 9# Copyright 2003 Pavel Machek <pavel@suse.cz>, distribute under GPLv2
10#
11# wakeup_code runs in real mode, and at unknown address (determined at run-time).
12# Therefore it must only use relative jumps/calls.
13#
14# Do we need to deal with A20? It is okay: ACPI specs says A20 must be enabled
15#
16# If physical address of wakeup_code is 0x12345, BIOS should call us with
17# cs = 0x1234, eip = 0x05
18#
19
20#define BEEP \
21 inb $97, %al; \
22 outb %al, $0x80; \
23 movb $3, %al; \
24 outb %al, $97; \
25 outb %al, $0x80; \
26 movb $-74, %al; \
27 outb %al, $67; \
28 outb %al, $0x80; \
29 movb $-119, %al; \
30 outb %al, $66; \
31 outb %al, $0x80; \
32 movb $15, %al; \
33 outb %al, $66;
34
35
36ALIGN
37 .align 16
38ENTRY(wakeup_start)
39wakeup_code:
40 wakeup_code_start = .
41 .code16
42
43# Running in *copy* of this code, somewhere in low 1MB.
44
45 cli
46 cld
47 # setup data segment
48 movw %cs, %ax
49 movw %ax, %ds # Make ds:0 point to wakeup_start
50 movw %ax, %ss
51
52 # Data segment must be set up before we can see whether to beep.
53 testl $4, realmode_flags - wakeup_code
54 jz 1f
55 BEEP
561:
57
58 # Private stack is needed for ASUS board
59 mov $(wakeup_stack - wakeup_code), %sp
60
61 pushl $0 # Kill any dangerous flags
62 popfl
63
64 movl real_magic - wakeup_code, %eax
65 cmpl $0x12345678, %eax
66 jne bogus_real_magic
67
68 testl $1, realmode_flags - wakeup_code
69 jz 1f
70 lcall $0xc000,$3
71 movw %cs, %ax
72 movw %ax, %ds # Bios might have played with that
73 movw %ax, %ss
741:
75
76 testl $2, realmode_flags - wakeup_code
77 jz 1f
78 mov video_mode - wakeup_code, %ax
79 call mode_set
801:
81
82 mov %ds, %ax # Find 32bit wakeup_code addr
83 movzx %ax, %esi # (Convert %ds:gdt to a liner ptr)
84 shll $4, %esi
85 # Fix up the vectors
86 addl %esi, wakeup_32_vector - wakeup_code
87 addl %esi, wakeup_long64_vector - wakeup_code
88 addl %esi, gdt_48a + 2 - wakeup_code # Fixup the gdt pointer
89
90 lidtl %ds:idt_48a - wakeup_code
91 lgdtl %ds:gdt_48a - wakeup_code # load gdt with whatever is
92 # appropriate
93
94 movl $1, %eax # protected mode (PE) bit
95 lmsw %ax # This is it!
96 jmp 1f
971:
98
99 ljmpl *(wakeup_32_vector - wakeup_code)
100
101 .balign 4
102wakeup_32_vector:
103 .long wakeup_32 - wakeup_code
104 .word __KERNEL32_CS, 0
105
106 .code32
107wakeup_32:
108# Running in this code, but at low address; paging is not yet turned on.
109
110 movl $__KERNEL_DS, %eax
111 movl %eax, %ds
112
113 /*
114 * Prepare for entering 64bits mode
115 */
116
117 /* Enable PAE */
118 xorl %eax, %eax
119 btsl $5, %eax
120 movl %eax, %cr4
121
122 /* Setup early boot stage 4 level pagetables */
123 leal (wakeup_level4_pgt - wakeup_code)(%esi), %eax
124 movl %eax, %cr3
125
126 /* Check if nx is implemented */
127 movl $0x80000001, %eax
128 cpuid
129 movl %edx,%edi
130
131 /* Enable Long Mode */
132 xorl %eax, %eax
133 btsl $_EFER_LME, %eax
134
135 /* No Execute supported? */
136 btl $20,%edi
137 jnc 1f
138 btsl $_EFER_NX, %eax
139
140 /* Make changes effective */
1411: movl $MSR_EFER, %ecx
142 xorl %edx, %edx
143 wrmsr
144
145 xorl %eax, %eax
146 btsl $31, %eax /* Enable paging and in turn activate Long Mode */
147 btsl $0, %eax /* Enable protected mode */
148
149 /* Make changes effective */
150 movl %eax, %cr0
151
152 /* At this point:
153 CR4.PAE must be 1
154 CS.L must be 0
155 CR3 must point to PML4
156 Next instruction must be a branch
157 This must be on identity-mapped page
158 */
159 /*
160 * At this point we're in long mode but in 32bit compatibility mode
161 * with EFER.LME = 1, CS.L = 0, CS.D = 1 (and in turn
162 * EFER.LMA = 1). Now we want to jump in 64bit mode, to do that we load
163 * the new gdt/idt that has __KERNEL_CS with CS.L = 1.
164 */
165
166 /* Finally jump in 64bit mode */
167 ljmp *(wakeup_long64_vector - wakeup_code)(%esi)
168
169 .balign 4
170wakeup_long64_vector:
171 .long wakeup_long64 - wakeup_code
172 .word __KERNEL_CS, 0
173 10
174.code64 11.code64
175
176 /* Hooray, we are in Long 64-bit mode (but still running in
177 * low memory)
178 */
179wakeup_long64:
180 /* 12 /*
181 * We must switch to a new descriptor in kernel space for the GDT 13 * Hooray, we are in Long 64-bit mode (but still running in low memory)
182 * because soon the kernel won't have access anymore to the userspace
183 * addresses where we're currently running on. We have to do that here
184 * because in 32bit we couldn't load a 64bit linear address.
185 */ 14 */
186 lgdt cpu_gdt_descr 15ENTRY(wakeup_long64)
187 16wakeup_long64:
188 movq saved_magic, %rax 17 movq saved_magic, %rax
189 movq $0x123456789abcdef0, %rdx 18 movq $0x123456789abcdef0, %rdx
190 cmpq %rdx, %rax 19 cmpq %rdx, %rax
191 jne bogus_64_magic 20 jne bogus_64_magic
192 21
193 nop
194 nop
195 movw $__KERNEL_DS, %ax 22 movw $__KERNEL_DS, %ax
196 movw %ax, %ss 23 movw %ax, %ss
197 movw %ax, %ds 24 movw %ax, %ds
@@ -208,130 +35,8 @@ wakeup_long64:
208 movq saved_rip, %rax 35 movq saved_rip, %rax
209 jmp *%rax 36 jmp *%rax
210 37
211.code32
212
213 .align 64
214gdta:
215 /* Its good to keep gdt in sync with one in trampoline.S */
216 .word 0, 0, 0, 0 # dummy
217 /* ??? Why I need the accessed bit set in order for this to work? */
218 .quad 0x00cf9b000000ffff # __KERNEL32_CS
219 .quad 0x00af9b000000ffff # __KERNEL_CS
220 .quad 0x00cf93000000ffff # __KERNEL_DS
221
222idt_48a:
223 .word 0 # idt limit = 0
224 .word 0, 0 # idt base = 0L
225
226gdt_48a:
227 .word 0x800 # gdt limit=2048,
228 # 256 GDT entries
229 .long gdta - wakeup_code # gdt base (relocated in later)
230
231real_magic: .quad 0
232video_mode: .quad 0
233realmode_flags: .quad 0
234
235.code16
236bogus_real_magic:
237 jmp bogus_real_magic
238
239.code64
240bogus_64_magic: 38bogus_64_magic:
241 jmp bogus_64_magic 39 jmp bogus_64_magic
242
243/* This code uses an extended set of video mode numbers. These include:
244 * Aliases for standard modes
245 * NORMAL_VGA (-1)
246 * EXTENDED_VGA (-2)
247 * ASK_VGA (-3)
248 * Video modes numbered by menu position -- NOT RECOMMENDED because of lack
249 * of compatibility when extending the table. These are between 0x00 and 0xff.
250 */
251#define VIDEO_FIRST_MENU 0x0000
252
253/* Standard BIOS video modes (BIOS number + 0x0100) */
254#define VIDEO_FIRST_BIOS 0x0100
255
256/* VESA BIOS video modes (VESA number + 0x0200) */
257#define VIDEO_FIRST_VESA 0x0200
258
259/* Video7 special modes (BIOS number + 0x0900) */
260#define VIDEO_FIRST_V7 0x0900
261
262# Setting of user mode (AX=mode ID) => CF=success
263
264# For now, we only handle VESA modes (0x0200..0x03ff). To handle other
265# modes, we should probably compile in the video code from the boot
266# directory.
267.code16
268mode_set:
269 movw %ax, %bx
270 subb $VIDEO_FIRST_VESA>>8, %bh
271 cmpb $2, %bh
272 jb check_vesa
273
274setbad:
275 clc
276 ret
277
278check_vesa:
279 orw $0x4000, %bx # Use linear frame buffer
280 movw $0x4f02, %ax # VESA BIOS mode set call
281 int $0x10
282 cmpw $0x004f, %ax # AL=4f if implemented
283 jnz setbad # AH=0 if OK
284
285 stc
286 ret
287
288wakeup_stack_begin: # Stack grows down
289
290.org 0xff0
291wakeup_stack: # Just below end of page
292
293.org 0x1000
294ENTRY(wakeup_level4_pgt)
295 .quad level3_ident_pgt - __START_KERNEL_map + _KERNPG_TABLE
296 .fill 510,8,0
297 /* (2^48-(2*1024*1024*1024))/(2^39) = 511 */
298 .quad level3_kernel_pgt - __START_KERNEL_map + _KERNPG_TABLE
299
300ENTRY(wakeup_end)
301
302##
303# acpi_copy_wakeup_routine
304#
305# Copy the above routine to low memory.
306#
307# Parameters:
308# %rdi: place to copy wakeup routine to
309#
310# Returned address is location of code in low memory (past data and stack)
311#
312 .code64
313ENTRY(acpi_copy_wakeup_routine)
314 pushq %rax
315 pushq %rdx
316
317 movl saved_video_mode, %edx
318 movl %edx, video_mode - wakeup_start (,%rdi)
319 movl acpi_realmode_flags, %edx
320 movl %edx, realmode_flags - wakeup_start (,%rdi)
321 movq $0x12345678, real_magic - wakeup_start (,%rdi)
322 movq $0x123456789abcdef0, %rdx
323 movq %rdx, saved_magic
324
325 movq saved_magic, %rax
326 movq $0x123456789abcdef0, %rdx
327 cmpq %rdx, %rax
328 jne bogus_64_magic
329
330 # restore the regs we used
331 popq %rdx
332 popq %rax
333ENTRY(do_suspend_lowlevel_s4bios)
334 ret
335 40
336 .align 2 41 .align 2
337 .p2align 4,,15 42 .p2align 4,,15
@@ -414,7 +119,7 @@ do_suspend_lowlevel:
414 jmp restore_processor_state 119 jmp restore_processor_state
415.LFE5: 120.LFE5:
416.Lfe5: 121.Lfe5:
417 .size do_suspend_lowlevel,.Lfe5-do_suspend_lowlevel 122 .size do_suspend_lowlevel, .Lfe5-do_suspend_lowlevel
418 123
419.data 124.data
420ALIGN 125ALIGN
diff --git a/arch/x86/kernel/acpi/wakeup_rm.S b/arch/x86/kernel/acpi/wakeup_rm.S
new file mode 100644
index 000000000000..6ff3b5730575
--- /dev/null
+++ b/arch/x86/kernel/acpi/wakeup_rm.S
@@ -0,0 +1,10 @@
1/*
2 * Wrapper script for the realmode binary as a transport object
3 * before copying to low memory.
4 */
5 .section ".rodata","a"
6 .globl wakeup_code_start, wakeup_code_end
7wakeup_code_start:
8 .incbin "arch/x86/kernel/acpi/realmode/wakeup.bin"
9wakeup_code_end:
10 .size wakeup_code_start, .-wakeup_code_start