aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/realmode
diff options
context:
space:
mode:
authorJarkko Sakkinen <jarkko.sakkinen@intel.com>2012-05-08 14:22:29 -0400
committerH. Peter Anvin <hpa@linux.intel.com>2012-05-08 14:46:05 -0400
commitc9b77ccb52a5c77233b0e557b7d4417b00ef4012 (patch)
tree152d1c9b60796db21458583a76b57f995c4cd3bf /arch/x86/realmode
parent48927bbb97c7d4cf343c05827ab9ac30c60678cb (diff)
x86, realmode: Move ACPI wakeup to unified realmode code
Migrated ACPI wakeup code to the real-mode blob. Code existing in .x86_trampoline can be completely removed. Static descriptor table in wakeup_asm.S is courtesy of H. Peter Anvin. Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@intel.com> Link: http://lkml.kernel.org/r/1336501366-28617-7-git-send-email-jarkko.sakkinen@intel.com Cc: Rafael J. Wysocki <rjw@sisk.pl> Cc: Len Brown <len.brown@intel.com> Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'arch/x86/realmode')
-rw-r--r--arch/x86/realmode/rm/Makefile4
-rw-r--r--arch/x86/realmode/rm/header.S5
-rw-r--r--arch/x86/realmode/rm/realmode.lds.S4
-rw-r--r--arch/x86/realmode/rm/wakeup/.gitignore3
-rw-r--r--arch/x86/realmode/rm/wakeup/Makefile33
-rw-r--r--arch/x86/realmode/rm/wakeup/bioscall.S1
-rw-r--r--arch/x86/realmode/rm/wakeup/copy.S1
-rw-r--r--arch/x86/realmode/rm/wakeup/regs.c1
-rw-r--r--arch/x86/realmode/rm/wakeup/video-bios.c1
-rw-r--r--arch/x86/realmode/rm/wakeup/video-mode.c1
-rw-r--r--arch/x86/realmode/rm/wakeup/video-vesa.c1
-rw-r--r--arch/x86/realmode/rm/wakeup/video-vga.c1
-rw-r--r--arch/x86/realmode/rm/wakeup/wakemain.c82
-rw-r--r--arch/x86/realmode/rm/wakeup/wakeup.h41
-rw-r--r--arch/x86/realmode/rm/wakeup/wakeup_asm.S189
15 files changed, 368 insertions, 0 deletions
diff --git a/arch/x86/realmode/rm/Makefile b/arch/x86/realmode/rm/Makefile
index 56ec64f94e69..2432acb6b04f 100644
--- a/arch/x86/realmode/rm/Makefile
+++ b/arch/x86/realmode/rm/Makefile
@@ -14,9 +14,13 @@ always := realmode.bin
14realmode-y += header.o 14realmode-y += header.o
15realmode-$(CONFIG_X86_32) += reboot_32.o 15realmode-$(CONFIG_X86_32) += reboot_32.o
16realmode-y += trampoline_$(BITS).o 16realmode-y += trampoline_$(BITS).o
17realmode-$(CONFIG_ACPI_SLEEP) += wakeup/wakeup.o
17 18
18targets += $(realmode-y) 19targets += $(realmode-y)
19 20
21$(obj)/wakeup/wakeup.o: FORCE
22 $(Q)$(MAKE) $(build)=$(obj)/wakeup $@
23
20REALMODE_OBJS = $(addprefix $(obj)/,$(realmode-y)) 24REALMODE_OBJS = $(addprefix $(obj)/,$(realmode-y))
21 25
22sed-pasyms := -n -r -e 's/^([0-9a-fA-F]+) [ABCDGRSTVW] (.+)$$/pa_\2 = \2;/p' 26sed-pasyms := -n -r -e 's/^([0-9a-fA-F]+) [ABCDGRSTVW] (.+)$$/pa_\2 = \2;/p'
diff --git a/arch/x86/realmode/rm/header.S b/arch/x86/realmode/rm/header.S
index a97900409c61..730b1316c099 100644
--- a/arch/x86/realmode/rm/header.S
+++ b/arch/x86/realmode/rm/header.S
@@ -27,4 +27,9 @@ ENTRY(real_mode_header)
27 .long pa_level3_ident_pgt 27 .long pa_level3_ident_pgt
28 .long pa_level3_kernel_pgt 28 .long pa_level3_kernel_pgt
29#endif 29#endif
30 /* ACPI sleep */
31#ifdef CONFIG_ACPI_SLEEP
32 .long pa_wakeup_start
33 .long pa_wakeup_header
34#endif
30END(real_mode_header) 35END(real_mode_header)
diff --git a/arch/x86/realmode/rm/realmode.lds.S b/arch/x86/realmode/rm/realmode.lds.S
index c5b8a4f31ba3..91b83ea55c37 100644
--- a/arch/x86/realmode/rm/realmode.lds.S
+++ b/arch/x86/realmode/rm/realmode.lds.S
@@ -25,6 +25,10 @@ SECTIONS
25 .rodata : { 25 .rodata : {
26 *(.rodata) 26 *(.rodata)
27 *(.rodata.*) 27 *(.rodata.*)
28 . = ALIGN(16);
29 video_cards = .;
30 *(.videocards)
31 video_cards_end = .;
28 } 32 }
29 33
30 . = ALIGN(PAGE_SIZE); 34 . = ALIGN(PAGE_SIZE);
diff --git a/arch/x86/realmode/rm/wakeup/.gitignore b/arch/x86/realmode/rm/wakeup/.gitignore
new file mode 100644
index 000000000000..58f1f48a58f8
--- /dev/null
+++ b/arch/x86/realmode/rm/wakeup/.gitignore
@@ -0,0 +1,3 @@
1wakeup.bin
2wakeup.elf
3wakeup.lds
diff --git a/arch/x86/realmode/rm/wakeup/Makefile b/arch/x86/realmode/rm/wakeup/Makefile
new file mode 100644
index 000000000000..4c8533240cdd
--- /dev/null
+++ b/arch/x86/realmode/rm/wakeup/Makefile
@@ -0,0 +1,33 @@
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
9always := wakeup.o
10
11wakeup-y += wakeup_asm.o wakemain.o video-mode.o
12wakeup-y += copy.o bioscall.o regs.o
13
14# The link order of the video-*.o modules can matter. In particular,
15# video-vga.o *must* be listed first, followed by video-vesa.o.
16# Hardware-specific drivers should follow in the order they should be
17# probed, and video-bios.o should typically be last.
18wakeup-y += video-vga.o
19wakeup-y += video-vesa.o
20wakeup-y += video-bios.o
21
22targets += $(wakeup-y)
23
24WAKEUP_OBJS = $(addprefix $(obj)/,$(wakeup-y))
25
26LDFLAGS_wakeup.o := -m elf_i386 -r
27$(obj)/wakeup.o: $(WAKEUP_OBJS) FORCE
28 $(call if_changed,ld)
29
30bootsrc := $(src)/../../../boot
31
32ccflags-y += -D_WAKEUP -I$(srctree)/$(bootsrc)
33asflags-y += -D_WAKEUP -I$(srctree)/$(bootsrc)
diff --git a/arch/x86/realmode/rm/wakeup/bioscall.S b/arch/x86/realmode/rm/wakeup/bioscall.S
new file mode 100644
index 000000000000..f51eb0bb56ce
--- /dev/null
+++ b/arch/x86/realmode/rm/wakeup/bioscall.S
@@ -0,0 +1 @@
#include "../../../boot/bioscall.S"
diff --git a/arch/x86/realmode/rm/wakeup/copy.S b/arch/x86/realmode/rm/wakeup/copy.S
new file mode 100644
index 000000000000..dc59ebee69d8
--- /dev/null
+++ b/arch/x86/realmode/rm/wakeup/copy.S
@@ -0,0 +1 @@
#include "../../../boot/copy.S"
diff --git a/arch/x86/realmode/rm/wakeup/regs.c b/arch/x86/realmode/rm/wakeup/regs.c
new file mode 100644
index 000000000000..6206033ba202
--- /dev/null
+++ b/arch/x86/realmode/rm/wakeup/regs.c
@@ -0,0 +1 @@
#include "../../../boot/regs.c"
diff --git a/arch/x86/realmode/rm/wakeup/video-bios.c b/arch/x86/realmode/rm/wakeup/video-bios.c
new file mode 100644
index 000000000000..7deabc144a27
--- /dev/null
+++ b/arch/x86/realmode/rm/wakeup/video-bios.c
@@ -0,0 +1 @@
#include "../../../boot/video-bios.c"
diff --git a/arch/x86/realmode/rm/wakeup/video-mode.c b/arch/x86/realmode/rm/wakeup/video-mode.c
new file mode 100644
index 000000000000..328ad209f113
--- /dev/null
+++ b/arch/x86/realmode/rm/wakeup/video-mode.c
@@ -0,0 +1 @@
#include "../../../boot/video-mode.c"
diff --git a/arch/x86/realmode/rm/wakeup/video-vesa.c b/arch/x86/realmode/rm/wakeup/video-vesa.c
new file mode 100644
index 000000000000..9dbb9672226a
--- /dev/null
+++ b/arch/x86/realmode/rm/wakeup/video-vesa.c
@@ -0,0 +1 @@
#include "../../../boot/video-vesa.c"
diff --git a/arch/x86/realmode/rm/wakeup/video-vga.c b/arch/x86/realmode/rm/wakeup/video-vga.c
new file mode 100644
index 000000000000..bcc81255f374
--- /dev/null
+++ b/arch/x86/realmode/rm/wakeup/video-vga.c
@@ -0,0 +1 @@
#include "../../../boot/video-vga.c"
diff --git a/arch/x86/realmode/rm/wakeup/wakemain.c b/arch/x86/realmode/rm/wakeup/wakemain.c
new file mode 100644
index 000000000000..91405d515ec6
--- /dev/null
+++ b/arch/x86/realmode/rm/wakeup/wakemain.c
@@ -0,0 +1,82 @@
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
71 if (wakeup_header.realmode_flags & 4)
72 send_morse("...-");
73
74 if (wakeup_header.realmode_flags & 1)
75 asm volatile("lcallw $0xc000,$3");
76
77 if (wakeup_header.realmode_flags & 2) {
78 /* Need to call BIOS */
79 probe_cards(0);
80 set_mode(wakeup_header.video_mode);
81 }
82}
diff --git a/arch/x86/realmode/rm/wakeup/wakeup.h b/arch/x86/realmode/rm/wakeup/wakeup.h
new file mode 100644
index 000000000000..2dfaf06b8af1
--- /dev/null
+++ b/arch/x86/realmode/rm/wakeup/wakeup.h
@@ -0,0 +1,41 @@
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 u32 pmode_entry; /* Protected mode resume point, 32-bit only */
16 u16 pmode_cs;
17 u32 pmode_cr0; /* Protected mode cr0 */
18 u32 pmode_cr3; /* Protected mode cr3 */
19 u32 pmode_cr4; /* Protected mode cr4 */
20 u32 pmode_efer_low; /* Protected mode EFER */
21 u32 pmode_efer_high;
22 u64 pmode_gdt;
23 u32 pmode_misc_en_low; /* Protected mode MISC_ENABLE */
24 u32 pmode_misc_en_high;
25 u32 pmode_behavior; /* Wakeup routine behavior flags */
26 u32 realmode_flags;
27 u32 real_magic;
28 u32 signature; /* To check we have correct structure */
29} __attribute__((__packed__));
30
31extern struct wakeup_header wakeup_header;
32#endif
33
34#define WAKEUP_HEADER_OFFSET 8
35#define WAKEUP_HEADER_SIGNATURE 0x51ee1111
36#define WAKEUP_END_SIGNATURE 0x65a22c82
37
38/* Wakeup behavior bits */
39#define WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE 0
40
41#endif /* ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H */
diff --git a/arch/x86/realmode/rm/wakeup/wakeup_asm.S b/arch/x86/realmode/rm/wakeup/wakeup_asm.S
new file mode 100644
index 000000000000..b61126cb599e
--- /dev/null
+++ b/arch/x86/realmode/rm/wakeup/wakeup_asm.S
@@ -0,0 +1,189 @@
1/*
2 * ACPI wakeup real mode startup stub
3 */
4#include <asm/segment.h>
5#include <asm/msr-index.h>
6#include <asm/page_types.h>
7#include <asm/pgtable_types.h>
8#include <asm/processor-flags.h>
9#include "wakeup.h"
10
11 .code16
12
13/* This should match the structure in wakeup.h */
14 .section ".data", "aw"
15 .globl wakeup_header
16wakeup_header:
17video_mode: .short 0 /* Video mode number */
18pmode_entry: .long 0
19pmode_cs: .short __KERNEL_CS
20pmode_cr0: .long 0 /* Saved %cr0 */
21pmode_cr3: .long 0 /* Saved %cr3 */
22pmode_cr4: .long 0 /* Saved %cr4 */
23pmode_efer: .quad 0 /* Saved EFER */
24pmode_gdt: .quad 0
25pmode_misc_en: .quad 0 /* Saved MISC_ENABLE MSR */
26pmode_behavior: .long 0 /* Wakeup behavior flags */
27realmode_flags: .long 0
28real_magic: .long 0
29signature: .long WAKEUP_HEADER_SIGNATURE
30 .size wakeup_header, .-wakeup_header
31
32 .text
33 .code16
34 .globl wakeup_start
35wakeup_start:
36 cli
37 cld
38
39 .byte 0xea /* ljmpw */
40 .word 3f
41 .word real_mode_seg
423:
43 /* Apparently some dimwit BIOS programmers don't know how to
44 program a PM to RM transition, and we might end up here with
45 junk in the data segment descriptor registers. The only way
46 to repair that is to go into PM and fix it ourselves... */
47 movw $16, %cx
48 lgdtl %cs:wakeup_gdt
49 movl %cr0, %eax
50 orb $X86_CR0_PE, %al
51 movl %eax, %cr0
52 ljmpw $8, $2f
532:
54 movw %cx, %ds
55 movw %cx, %es
56 movw %cx, %ss
57 movw %cx, %fs
58 movw %cx, %gs
59
60 andb $~X86_CR0_PE, %al
61 movl %eax, %cr0
62 .byte 0xea /* ljmpw */
63 .word 3f
64 .word real_mode_seg
653:
66 /* Set up segments */
67 movw %cs, %ax
68 movw %ax, %ds
69 movw %ax, %es
70 movw %ax, %ss
71 lidtl wakeup_idt
72
73 movl $wakeup_stack_end, %esp
74
75 /* Clear the EFLAGS */
76 pushl $0
77 popfl
78
79 /* Check header signature... */
80 movl signature, %eax
81 cmpl $WAKEUP_HEADER_SIGNATURE, %eax
82 jne bogus_real_magic
83
84 /* Check we really have everything... */
85 movl end_signature, %eax
86 cmpl $WAKEUP_END_SIGNATURE, %eax
87 jne bogus_real_magic
88
89 /* Call the C code */
90 calll main
91
92 /* Restore MISC_ENABLE before entering protected mode, in case
93 BIOS decided to clear XD_DISABLE during S3. */
94 movl pmode_behavior, %eax
95 btl $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %eax
96 jnc 1f
97
98 movl pmode_misc_en, %eax
99 movl pmode_misc_en + 4, %edx
100 movl $MSR_IA32_MISC_ENABLE, %ecx
101 wrmsr
1021:
103
104 /* Do any other stuff... */
105
106#ifndef CONFIG_64BIT
107 /* This could also be done in C code... */
108 movl pmode_cr3, %eax
109 movl %eax, %cr3
110
111 movl pmode_cr4, %ecx
112 jecxz 1f
113 movl %ecx, %cr4
1141:
115 movl pmode_efer, %eax
116 movl pmode_efer + 4, %edx
117 movl %eax, %ecx
118 orl %edx, %ecx
119 jz 1f
120 movl $MSR_EFER, %ecx
121 wrmsr
1221:
123
124 lgdtl pmode_gdt
125
126 /* This really couldn't... */
127 movl pmode_cr0, %eax
128 movl %eax, %cr0
129 ljmpl *pmode_entry
130#else
131 jmp trampoline_data
132#endif
133
134bogus_real_magic:
1351:
136 hlt
137 jmp 1b
138
139 .section ".rodata","a"
140
141 /*
142 * Set up the wakeup GDT. We set these up as Big Real Mode,
143 * that is, with limits set to 4 GB. At least the Lenovo
144 * Thinkpad X61 is known to need this for the video BIOS
145 * initialization quirk to work; this is likely to also
146 * be the case for other laptops or integrated video devices.
147 */
148
149 .globl wakeup_gdt
150 .balign 16
151wakeup_gdt:
152 .word 3*8-1 /* Self-descriptor */
153 .long pa_wakeup_gdt
154 .word 0
155
156 .word 0xffff /* 16-bit code segment @ real_mode_base */
157 .long 0x9b000000 + pa_real_mode_base
158 .word 0x008f /* big real mode */
159
160 .word 0xffff /* 16-bit data segment @ real_mode_base */
161 .long 0x93000000 + pa_real_mode_base
162 .word 0x008f /* big real mode */
163 .size wakeup_gdt, .-wakeup_gdt
164
165 .data
166 .balign 8
167
168 /* This is the standard real-mode IDT */
169wakeup_idt:
170 .word 0xffff /* limit */
171 .long 0 /* address */
172 .word 0
173
174 .globl HEAP, heap_end
175HEAP:
176 .long wakeup_heap
177heap_end:
178 .long wakeup_stack
179
180 .bss
181wakeup_heap:
182 .space 2048
183wakeup_stack:
184 .space 2048
185wakeup_stack_end:
186
187 .section ".signature","a"
188end_signature:
189 .long WAKEUP_END_SIGNATURE