diff options
Diffstat (limited to 'arch/x86/kernel/acpi')
-rw-r--r-- | arch/x86/kernel/acpi/Makefile | 9 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/realmode/Makefile | 57 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/realmode/copy.S | 1 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/realmode/video-bios.c | 1 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/realmode/video-mode.c | 1 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/realmode/video-vesa.c | 1 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/realmode/video-vga.c | 1 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/realmode/wakemain.c | 81 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/realmode/wakeup.S | 113 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/realmode/wakeup.h | 36 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/realmode/wakeup.lds.S | 61 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/sleep.c | 71 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/sleep.h | 16 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/sleep_32.c | 40 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/wakeup_32.S | 247 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/wakeup_64.S | 313 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/wakeup_rm.S | 10 |
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 @@ | |||
1 | subdir- := realmode | ||
2 | |||
1 | obj-$(CONFIG_ACPI) += boot.o | 3 | obj-$(CONFIG_ACPI) += boot.o |
2 | obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o | 4 | obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_rm.o wakeup_$(BITS).o |
3 | 5 | ||
4 | ifneq ($(CONFIG_ACPI_PROCESSOR),) | 6 | ifneq ($(CONFIG_ACPI_PROCESSOR),) |
5 | obj-y += cstate.o processor.o | 7 | obj-y += cstate.o processor.o |
6 | endif | 8 | endif |
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 | |||
9 | targets := wakeup.bin wakeup.elf | ||
10 | |||
11 | wakeup-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. | ||
17 | wakeup-y += video-vga.o | ||
18 | wakeup-y += video-vesa.o | ||
19 | wakeup-y += video-bios.o | ||
20 | |||
21 | targets += $(wakeup-y) | ||
22 | |||
23 | bootsrc := $(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. | ||
30 | KBUILD_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) | ||
42 | KBUILD_CFLAGS += $(call cc-option, -m32) | ||
43 | KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__ | ||
44 | |||
45 | WAKEUP_OBJS = $(addprefix $(obj)/,$(wakeup-y)) | ||
46 | |||
47 | LDFLAGS_wakeup.elf := -T | ||
48 | |||
49 | CPPFLAGS_wakeup.lds += -P -C | ||
50 | |||
51 | $(obj)/wakeup.elf: $(src)/wakeup.lds $(WAKEUP_OBJS) FORCE | ||
52 | $(call if_changed,ld) | ||
53 | |||
54 | OBJCOPYFLAGS_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 | |||
4 | static void udelay(int loops) | ||
5 | { | ||
6 | while (loops--) | ||
7 | io_delay(); /* Approximately 1 us */ | ||
8 | } | ||
9 | |||
10 | static 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. */ | ||
39 | static 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 | |||
64 | void 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 | ||
14 | wakeup_header: | ||
15 | video_mode: .short 0 /* Video mode number */ | ||
16 | pmode_return: .byte 0x66, 0xea /* ljmpl */ | ||
17 | .long 0 /* offset goes here */ | ||
18 | .short __KERNEL_CS | ||
19 | pmode_cr0: .long 0 /* Saved %cr0 */ | ||
20 | pmode_cr3: .long 0 /* Saved %cr3 */ | ||
21 | pmode_cr4: .long 0 /* Saved %cr4 */ | ||
22 | pmode_efer: .quad 0 /* Saved EFER */ | ||
23 | pmode_gdt: .quad 0 | ||
24 | realmode_flags: .long 0 | ||
25 | real_magic: .long 0 | ||
26 | trampoline_segment: .word 0 | ||
27 | signature: .long 0x51ee1111 | ||
28 | |||
29 | .text | ||
30 | .globl _start | ||
31 | .code16 | ||
32 | wakeup_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 | ||
72 | 1: | ||
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 | ||
80 | 1: | ||
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 | |||
95 | bogus_real_magic: | ||
96 | 1: | ||
97 | hlt | ||
98 | jmp 1b | ||
99 | |||
100 | .data | ||
101 | .balign 4 | ||
102 | .globl HEAP, heap_end | ||
103 | HEAP: | ||
104 | .long wakeup_heap | ||
105 | heap_end: | ||
106 | .long wakeup_stack | ||
107 | |||
108 | .bss | ||
109 | wakeup_heap: | ||
110 | .space 2048 | ||
111 | wakeup_stack: | ||
112 | .space 2048 | ||
113 | wakeup_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 */ | ||
13 | struct 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 | |||
30 | extern 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 | |||
9 | OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") | ||
10 | OUTPUT_ARCH(i386) | ||
11 | ENTRY(_start) | ||
12 | |||
13 | SECTIONS | ||
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. */ | ||
16 | unsigned long acpi_wakeup_address; | 16 | unsigned long acpi_wakeup_address; |
17 | unsigned long acpi_realmode_flags; | 17 | unsigned long acpi_realmode_flags; |
18 | extern char wakeup_start, wakeup_end; | ||
19 | 18 | ||
20 | extern unsigned long acpi_copy_wakeup_routine(unsigned long); | 19 | /* address in low memory of the wakeup routine. */ |
20 | static unsigned long acpi_realmode; | ||
21 | |||
22 | #ifdef CONFIG_64BIT | ||
23 | static 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 | */ |
28 | int acpi_save_state_mem(void) | 34 | int 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 | */ |
57 | void __init acpi_reserve_bootmem(void) | 99 | void __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 | |||
7 | extern char wakeup_code_start, wakeup_code_end; | ||
8 | |||
9 | extern unsigned long saved_video_mode; | ||
10 | extern long saved_magic; | ||
11 | |||
12 | extern int wakeup_pmode_return; | ||
13 | extern char swsusp_pg_dir[PAGE_SIZE]; | ||
14 | |||
15 | extern unsigned long acpi_copy_wakeup_routine(unsigned long); | ||
16 | extern 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 */ | ||
17 | static __init int reset_videomode_after_s3(const struct dmi_system_id *d) | ||
18 | { | ||
19 | acpi_realmode_flags |= 2; | ||
20 | return 0; | ||
21 | } | ||
22 | |||
23 | static __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 | |||
34 | static int __init acpisleep_dmi_init(void) | ||
35 | { | ||
36 | dmi_check_system(acpisleep_dmi_table); | ||
37 | return 0; | ||
38 | } | ||
39 | |||
40 | core_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 | |||
31 | ALIGN | ||
32 | .align 4096 | ||
33 | ENTRY(wakeup_start) | ||
34 | wakeup_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 | ||
49 | 1: | ||
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 | ||
65 | 1: | ||
66 | |||
67 | testl $2, realmode_flags - wakeup_code | ||
68 | jz 1f | ||
69 | mov video_mode - wakeup_code, %ax | ||
70 | call mode_set | ||
71 | 1: | ||
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 | ||
84 | 4: | ||
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 | ||
96 | 1: | ||
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 | ||
104 | 1: | ||
105 | ljmpl $__KERNEL_CS, $wakeup_pmode_return | ||
106 | |||
107 | real_save_gdt: .word 0 | ||
108 | .long 0 | ||
109 | real_save_cr0: .long 0 | ||
110 | real_save_cr3: .long 0 | ||
111 | real_save_cr4: .long 0 | ||
112 | real_magic: .long 0 | ||
113 | video_mode: .long 0 | ||
114 | realmode_flags: .long 0 | ||
115 | real_efer_save_restore: .long 0 | ||
116 | real_save_efer_edx: .long 0 | ||
117 | real_save_efer_eax: .long 0 | ||
118 | |||
119 | bogus_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. | ||
146 | mode_set: | ||
147 | movw %ax, %bx | ||
148 | subb $VIDEO_FIRST_VESA>>8, %bh | ||
149 | cmpb $2, %bh | ||
150 | jb check_vesa | ||
151 | |||
152 | setbad: | ||
153 | clc | ||
154 | ret | ||
155 | |||
156 | check_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 | 11 | ENTRY(wakeup_pmode_return) |
170 | wakeup_stack_begin: # Stack grows down | ||
171 | |||
172 | .org 0xff0 # Just below end of page | ||
173 | wakeup_stack: | ||
174 | ENTRY(wakeup_end) | ||
175 | |||
176 | .org 0x1000 | ||
177 | |||
178 | wakeup_pmode_return: | 12 | wakeup_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 |
191 | 1: | 25 | 1: |
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 | ||
207 | bogus_magic: | 41 | bogus_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 | # | ||
221 | ENTRY(acpi_copy_wakeup_routine) | ||
222 | 45 | ||
223 | pushl %ebx | 46 | save_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 | ||
241 | 2: | ||
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 | |||
260 | save_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 | ||
273 | restore_registers: | 65 | restore_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 | ||
281 | ENTRY(do_suspend_lowlevel) | 74 | ENTRY(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 | |||
36 | ALIGN | ||
37 | .align 16 | ||
38 | ENTRY(wakeup_start) | ||
39 | wakeup_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 | ||
56 | 1: | ||
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 | ||
74 | 1: | ||
75 | |||
76 | testl $2, realmode_flags - wakeup_code | ||
77 | jz 1f | ||
78 | mov video_mode - wakeup_code, %ax | ||
79 | call mode_set | ||
80 | 1: | ||
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 | ||
97 | 1: | ||
98 | |||
99 | ljmpl *(wakeup_32_vector - wakeup_code) | ||
100 | |||
101 | .balign 4 | ||
102 | wakeup_32_vector: | ||
103 | .long wakeup_32 - wakeup_code | ||
104 | .word __KERNEL32_CS, 0 | ||
105 | |||
106 | .code32 | ||
107 | wakeup_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 */ | ||
141 | 1: 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 | ||
170 | wakeup_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 | */ | ||
179 | wakeup_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 | 15 | ENTRY(wakeup_long64) |
187 | 16 | wakeup_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 | ||
214 | gdta: | ||
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 | |||
222 | idt_48a: | ||
223 | .word 0 # idt limit = 0 | ||
224 | .word 0, 0 # idt base = 0L | ||
225 | |||
226 | gdt_48a: | ||
227 | .word 0x800 # gdt limit=2048, | ||
228 | # 256 GDT entries | ||
229 | .long gdta - wakeup_code # gdt base (relocated in later) | ||
230 | |||
231 | real_magic: .quad 0 | ||
232 | video_mode: .quad 0 | ||
233 | realmode_flags: .quad 0 | ||
234 | |||
235 | .code16 | ||
236 | bogus_real_magic: | ||
237 | jmp bogus_real_magic | ||
238 | |||
239 | .code64 | ||
240 | bogus_64_magic: | 38 | bogus_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 | ||
268 | mode_set: | ||
269 | movw %ax, %bx | ||
270 | subb $VIDEO_FIRST_VESA>>8, %bh | ||
271 | cmpb $2, %bh | ||
272 | jb check_vesa | ||
273 | |||
274 | setbad: | ||
275 | clc | ||
276 | ret | ||
277 | |||
278 | check_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 | |||
288 | wakeup_stack_begin: # Stack grows down | ||
289 | |||
290 | .org 0xff0 | ||
291 | wakeup_stack: # Just below end of page | ||
292 | |||
293 | .org 0x1000 | ||
294 | ENTRY(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 | |||
300 | ENTRY(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 | ||
313 | ENTRY(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 | ||
333 | ENTRY(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 |
420 | ALIGN | 125 | ALIGN |
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 | ||
7 | wakeup_code_start: | ||
8 | .incbin "arch/x86/kernel/acpi/realmode/wakeup.bin" | ||
9 | wakeup_code_end: | ||
10 | .size wakeup_code_start, .-wakeup_code_start | ||