diff options
34 files changed, 711 insertions, 773 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 0a7193ae45ed..4e32b6f7d31a 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -181,7 +181,7 @@ config X86_BIOS_REBOOT | |||
181 | 181 | ||
182 | config X86_TRAMPOLINE | 182 | config X86_TRAMPOLINE |
183 | bool | 183 | bool |
184 | depends on X86_SMP || (X86_VOYAGER && SMP) | 184 | depends on X86_SMP || (X86_VOYAGER && SMP) || (64BIT && ACPI_SLEEP) |
185 | default y | 185 | default y |
186 | 186 | ||
187 | config KTIME_SCALAR | 187 | config KTIME_SCALAR |
diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile index 9695affeb584..7ee102f9c4f8 100644 --- a/arch/x86/boot/Makefile +++ b/arch/x86/boot/Makefile | |||
@@ -30,7 +30,7 @@ subdir- := compressed | |||
30 | 30 | ||
31 | setup-y += a20.o cmdline.o copy.o cpu.o cpucheck.o edd.o | 31 | setup-y += a20.o cmdline.o copy.o cpu.o cpucheck.o edd.o |
32 | setup-y += header.o main.o mca.o memory.o pm.o pmjump.o | 32 | setup-y += header.o main.o mca.o memory.o pm.o pmjump.o |
33 | setup-y += printf.o string.o tty.o video.o version.o | 33 | setup-y += printf.o string.o tty.o video.o video-mode.o version.o |
34 | setup-$(CONFIG_X86_APM_BOOT) += apm.o | 34 | setup-$(CONFIG_X86_APM_BOOT) += apm.o |
35 | setup-$(CONFIG_X86_VOYAGER) += voyager.o | 35 | setup-$(CONFIG_X86_VOYAGER) += voyager.o |
36 | 36 | ||
diff --git a/arch/x86/boot/boot.h b/arch/x86/boot/boot.h index 7822a4983da2..09578070bfba 100644 --- a/arch/x86/boot/boot.h +++ b/arch/x86/boot/boot.h | |||
@@ -286,6 +286,11 @@ int getchar_timeout(void); | |||
286 | /* video.c */ | 286 | /* video.c */ |
287 | void set_video(void); | 287 | void set_video(void); |
288 | 288 | ||
289 | /* video-mode.c */ | ||
290 | int set_mode(u16 mode); | ||
291 | int mode_defined(u16 mode); | ||
292 | void probe_cards(int unsafe); | ||
293 | |||
289 | /* video-vesa.c */ | 294 | /* video-vesa.c */ |
290 | void vesa_store_edid(void); | 295 | void vesa_store_edid(void); |
291 | 296 | ||
diff --git a/arch/x86/boot/video-bios.c b/arch/x86/boot/video-bios.c index ff664a117096..39e247e96172 100644 --- a/arch/x86/boot/video-bios.c +++ b/arch/x86/boot/video-bios.c | |||
@@ -50,6 +50,7 @@ static int set_bios_mode(u8 mode) | |||
50 | if (new_mode == mode) | 50 | if (new_mode == mode) |
51 | return 0; /* Mode change OK */ | 51 | return 0; /* Mode change OK */ |
52 | 52 | ||
53 | #ifndef _WAKEUP | ||
53 | if (new_mode != boot_params.screen_info.orig_video_mode) { | 54 | if (new_mode != boot_params.screen_info.orig_video_mode) { |
54 | /* Mode setting failed, but we didn't end up where we | 55 | /* Mode setting failed, but we didn't end up where we |
55 | started. That's bad. Try to revert to the original | 56 | started. That's bad. Try to revert to the original |
@@ -59,13 +60,18 @@ static int set_bios_mode(u8 mode) | |||
59 | : "+a" (ax) | 60 | : "+a" (ax) |
60 | : : "ebx", "ecx", "edx", "esi", "edi"); | 61 | : : "ebx", "ecx", "edx", "esi", "edi"); |
61 | } | 62 | } |
63 | #endif | ||
62 | return -1; | 64 | return -1; |
63 | } | 65 | } |
64 | 66 | ||
65 | static int bios_probe(void) | 67 | static int bios_probe(void) |
66 | { | 68 | { |
67 | u8 mode; | 69 | u8 mode; |
70 | #ifdef _WAKEUP | ||
71 | u8 saved_mode = 0x03; | ||
72 | #else | ||
68 | u8 saved_mode = boot_params.screen_info.orig_video_mode; | 73 | u8 saved_mode = boot_params.screen_info.orig_video_mode; |
74 | #endif | ||
69 | u16 crtc; | 75 | u16 crtc; |
70 | struct mode_info *mi; | 76 | struct mode_info *mi; |
71 | int nmodes = 0; | 77 | int nmodes = 0; |
diff --git a/arch/x86/boot/video-mode.c b/arch/x86/boot/video-mode.c new file mode 100644 index 000000000000..748e8d06290a --- /dev/null +++ b/arch/x86/boot/video-mode.c | |||
@@ -0,0 +1,173 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007-2008 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/video-mode.c | ||
13 | * | ||
14 | * Set the video mode. This is separated out into a different | ||
15 | * file in order to be shared with the ACPI wakeup code. | ||
16 | */ | ||
17 | |||
18 | #include "boot.h" | ||
19 | #include "video.h" | ||
20 | #include "vesa.h" | ||
21 | |||
22 | /* | ||
23 | * Common variables | ||
24 | */ | ||
25 | int adapter; /* 0=CGA/MDA/HGC, 1=EGA, 2=VGA+ */ | ||
26 | u16 video_segment; | ||
27 | int force_x, force_y; /* Don't query the BIOS for cols/rows */ | ||
28 | |||
29 | int do_restore; /* Screen contents changed during mode flip */ | ||
30 | int graphic_mode; /* Graphic mode with linear frame buffer */ | ||
31 | |||
32 | /* Probe the video drivers and have them generate their mode lists. */ | ||
33 | void probe_cards(int unsafe) | ||
34 | { | ||
35 | struct card_info *card; | ||
36 | static u8 probed[2]; | ||
37 | |||
38 | if (probed[unsafe]) | ||
39 | return; | ||
40 | |||
41 | probed[unsafe] = 1; | ||
42 | |||
43 | for (card = video_cards; card < video_cards_end; card++) { | ||
44 | if (card->unsafe == unsafe) { | ||
45 | if (card->probe) | ||
46 | card->nmodes = card->probe(); | ||
47 | else | ||
48 | card->nmodes = 0; | ||
49 | } | ||
50 | } | ||
51 | } | ||
52 | |||
53 | /* Test if a mode is defined */ | ||
54 | int mode_defined(u16 mode) | ||
55 | { | ||
56 | struct card_info *card; | ||
57 | struct mode_info *mi; | ||
58 | int i; | ||
59 | |||
60 | for (card = video_cards; card < video_cards_end; card++) { | ||
61 | mi = card->modes; | ||
62 | for (i = 0; i < card->nmodes; i++, mi++) { | ||
63 | if (mi->mode == mode) | ||
64 | return 1; | ||
65 | } | ||
66 | } | ||
67 | |||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | /* Set mode (without recalc) */ | ||
72 | static int raw_set_mode(u16 mode, u16 *real_mode) | ||
73 | { | ||
74 | int nmode, i; | ||
75 | struct card_info *card; | ||
76 | struct mode_info *mi; | ||
77 | |||
78 | /* Drop the recalc bit if set */ | ||
79 | mode &= ~VIDEO_RECALC; | ||
80 | |||
81 | /* Scan for mode based on fixed ID, position, or resolution */ | ||
82 | nmode = 0; | ||
83 | for (card = video_cards; card < video_cards_end; card++) { | ||
84 | mi = card->modes; | ||
85 | for (i = 0; i < card->nmodes; i++, mi++) { | ||
86 | int visible = mi->x || mi->y; | ||
87 | |||
88 | if ((mode == nmode && visible) || | ||
89 | mode == mi->mode || | ||
90 | mode == (mi->y << 8)+mi->x) { | ||
91 | *real_mode = mi->mode; | ||
92 | return card->set_mode(mi); | ||
93 | } | ||
94 | |||
95 | if (visible) | ||
96 | nmode++; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | /* Nothing found? Is it an "exceptional" (unprobed) mode? */ | ||
101 | for (card = video_cards; card < video_cards_end; card++) { | ||
102 | if (mode >= card->xmode_first && | ||
103 | mode < card->xmode_first+card->xmode_n) { | ||
104 | struct mode_info mix; | ||
105 | *real_mode = mix.mode = mode; | ||
106 | mix.x = mix.y = 0; | ||
107 | return card->set_mode(&mix); | ||
108 | } | ||
109 | } | ||
110 | |||
111 | /* Otherwise, failure... */ | ||
112 | return -1; | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * Recalculate the vertical video cutoff (hack!) | ||
117 | */ | ||
118 | static void vga_recalc_vertical(void) | ||
119 | { | ||
120 | unsigned int font_size, rows; | ||
121 | u16 crtc; | ||
122 | u8 pt, ov; | ||
123 | |||
124 | set_fs(0); | ||
125 | font_size = rdfs8(0x485); /* BIOS: font size (pixels) */ | ||
126 | rows = force_y ? force_y : rdfs8(0x484)+1; /* Text rows */ | ||
127 | |||
128 | rows *= font_size; /* Visible scan lines */ | ||
129 | rows--; /* ... minus one */ | ||
130 | |||
131 | crtc = vga_crtc(); | ||
132 | |||
133 | pt = in_idx(crtc, 0x11); | ||
134 | pt &= ~0x80; /* Unlock CR0-7 */ | ||
135 | out_idx(pt, crtc, 0x11); | ||
136 | |||
137 | out_idx((u8)rows, crtc, 0x12); /* Lower height register */ | ||
138 | |||
139 | ov = in_idx(crtc, 0x07); /* Overflow register */ | ||
140 | ov &= 0xbd; | ||
141 | ov |= (rows >> (8-1)) & 0x02; | ||
142 | ov |= (rows >> (9-6)) & 0x40; | ||
143 | out_idx(ov, crtc, 0x07); | ||
144 | } | ||
145 | |||
146 | /* Set mode (with recalc if specified) */ | ||
147 | int set_mode(u16 mode) | ||
148 | { | ||
149 | int rv; | ||
150 | u16 real_mode; | ||
151 | |||
152 | /* Very special mode numbers... */ | ||
153 | if (mode == VIDEO_CURRENT_MODE) | ||
154 | return 0; /* Nothing to do... */ | ||
155 | else if (mode == NORMAL_VGA) | ||
156 | mode = VIDEO_80x25; | ||
157 | else if (mode == EXTENDED_VGA) | ||
158 | mode = VIDEO_8POINT; | ||
159 | |||
160 | rv = raw_set_mode(mode, &real_mode); | ||
161 | if (rv) | ||
162 | return rv; | ||
163 | |||
164 | if (mode & VIDEO_RECALC) | ||
165 | vga_recalc_vertical(); | ||
166 | |||
167 | /* Save the canonical mode number for the kernel, not | ||
168 | an alias, size specification or menu position */ | ||
169 | #ifndef _WAKEUP | ||
170 | boot_params.hdr.vid_mode = real_mode; | ||
171 | #endif | ||
172 | return 0; | ||
173 | } | ||
diff --git a/arch/x86/boot/video-vesa.c b/arch/x86/boot/video-vesa.c index 419b5c273374..5d5a3f6e8b5c 100644 --- a/arch/x86/boot/video-vesa.c +++ b/arch/x86/boot/video-vesa.c | |||
@@ -24,7 +24,11 @@ static struct vesa_mode_info vminfo; | |||
24 | 24 | ||
25 | __videocard video_vesa; | 25 | __videocard video_vesa; |
26 | 26 | ||
27 | #ifndef _WAKEUP | ||
27 | static void vesa_store_mode_params_graphics(void); | 28 | static void vesa_store_mode_params_graphics(void); |
29 | #else /* _WAKEUP */ | ||
30 | static inline void vesa_store_mode_params_graphics(void) {} | ||
31 | #endif /* _WAKEUP */ | ||
28 | 32 | ||
29 | static int vesa_probe(void) | 33 | static int vesa_probe(void) |
30 | { | 34 | { |
@@ -165,6 +169,8 @@ static int vesa_set_mode(struct mode_info *mode) | |||
165 | } | 169 | } |
166 | 170 | ||
167 | 171 | ||
172 | #ifndef _WAKEUP | ||
173 | |||
168 | /* Switch DAC to 8-bit mode */ | 174 | /* Switch DAC to 8-bit mode */ |
169 | static void vesa_dac_set_8bits(void) | 175 | static void vesa_dac_set_8bits(void) |
170 | { | 176 | { |
@@ -288,6 +294,8 @@ void vesa_store_edid(void) | |||
288 | #endif /* CONFIG_FIRMWARE_EDID */ | 294 | #endif /* CONFIG_FIRMWARE_EDID */ |
289 | } | 295 | } |
290 | 296 | ||
297 | #endif /* not _WAKEUP */ | ||
298 | |||
291 | __videocard video_vesa = | 299 | __videocard video_vesa = |
292 | { | 300 | { |
293 | .card_name = "VESA", | 301 | .card_name = "VESA", |
diff --git a/arch/x86/boot/video-vga.c b/arch/x86/boot/video-vga.c index 7259387b7d19..330d6589a2ad 100644 --- a/arch/x86/boot/video-vga.c +++ b/arch/x86/boot/video-vga.c | |||
@@ -210,6 +210,8 @@ static int vga_set_mode(struct mode_info *mode) | |||
210 | */ | 210 | */ |
211 | static int vga_probe(void) | 211 | static int vga_probe(void) |
212 | { | 212 | { |
213 | u16 ega_bx; | ||
214 | |||
213 | static const char *card_name[] = { | 215 | static const char *card_name[] = { |
214 | "CGA/MDA/HGC", "EGA", "VGA" | 216 | "CGA/MDA/HGC", "EGA", "VGA" |
215 | }; | 217 | }; |
@@ -226,12 +228,16 @@ static int vga_probe(void) | |||
226 | u8 vga_flag; | 228 | u8 vga_flag; |
227 | 229 | ||
228 | asm(INT10 | 230 | asm(INT10 |
229 | : "=b" (boot_params.screen_info.orig_video_ega_bx) | 231 | : "=b" (ega_bx) |
230 | : "a" (0x1200), "b" (0x10) /* Check EGA/VGA */ | 232 | : "a" (0x1200), "b" (0x10) /* Check EGA/VGA */ |
231 | : "ecx", "edx", "esi", "edi"); | 233 | : "ecx", "edx", "esi", "edi"); |
232 | 234 | ||
235 | #ifndef _WAKEUP | ||
236 | boot_params.screen_info.orig_video_ega_bx = ega_bx; | ||
237 | #endif | ||
238 | |||
233 | /* If we have MDA/CGA/HGC then BL will be unchanged at 0x10 */ | 239 | /* If we have MDA/CGA/HGC then BL will be unchanged at 0x10 */ |
234 | if ((u8)boot_params.screen_info.orig_video_ega_bx != 0x10) { | 240 | if ((u8)ega_bx != 0x10) { |
235 | /* EGA/VGA */ | 241 | /* EGA/VGA */ |
236 | asm(INT10 | 242 | asm(INT10 |
237 | : "=a" (vga_flag) | 243 | : "=a" (vga_flag) |
@@ -240,7 +246,9 @@ static int vga_probe(void) | |||
240 | 246 | ||
241 | if (vga_flag == 0x1a) { | 247 | if (vga_flag == 0x1a) { |
242 | adapter = ADAPTER_VGA; | 248 | adapter = ADAPTER_VGA; |
249 | #ifndef _WAKEUP | ||
243 | boot_params.screen_info.orig_video_isVGA = 1; | 250 | boot_params.screen_info.orig_video_isVGA = 1; |
251 | #endif | ||
244 | } else { | 252 | } else { |
245 | adapter = ADAPTER_EGA; | 253 | adapter = ADAPTER_EGA; |
246 | } | 254 | } |
diff --git a/arch/x86/boot/video.c b/arch/x86/boot/video.c index 696d08f3843c..c1c47ba069ef 100644 --- a/arch/x86/boot/video.c +++ b/arch/x86/boot/video.c | |||
@@ -18,21 +18,6 @@ | |||
18 | #include "video.h" | 18 | #include "video.h" |
19 | #include "vesa.h" | 19 | #include "vesa.h" |
20 | 20 | ||
21 | /* | ||
22 | * Mode list variables | ||
23 | */ | ||
24 | static struct card_info cards[]; /* List of cards to probe for */ | ||
25 | |||
26 | /* | ||
27 | * Common variables | ||
28 | */ | ||
29 | int adapter; /* 0=CGA/MDA/HGC, 1=EGA, 2=VGA+ */ | ||
30 | u16 video_segment; | ||
31 | int force_x, force_y; /* Don't query the BIOS for cols/rows */ | ||
32 | |||
33 | int do_restore = 0; /* Screen contents changed during mode flip */ | ||
34 | int graphic_mode; /* Graphic mode with linear frame buffer */ | ||
35 | |||
36 | static void store_cursor_position(void) | 21 | static void store_cursor_position(void) |
37 | { | 22 | { |
38 | u16 curpos; | 23 | u16 curpos; |
@@ -107,147 +92,6 @@ static void store_mode_params(void) | |||
107 | boot_params.screen_info.orig_video_lines = y; | 92 | boot_params.screen_info.orig_video_lines = y; |
108 | } | 93 | } |
109 | 94 | ||
110 | /* Probe the video drivers and have them generate their mode lists. */ | ||
111 | static void probe_cards(int unsafe) | ||
112 | { | ||
113 | struct card_info *card; | ||
114 | static u8 probed[2]; | ||
115 | |||
116 | if (probed[unsafe]) | ||
117 | return; | ||
118 | |||
119 | probed[unsafe] = 1; | ||
120 | |||
121 | for (card = video_cards; card < video_cards_end; card++) { | ||
122 | if (card->unsafe == unsafe) { | ||
123 | if (card->probe) | ||
124 | card->nmodes = card->probe(); | ||
125 | else | ||
126 | card->nmodes = 0; | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | |||
131 | /* Test if a mode is defined */ | ||
132 | int mode_defined(u16 mode) | ||
133 | { | ||
134 | struct card_info *card; | ||
135 | struct mode_info *mi; | ||
136 | int i; | ||
137 | |||
138 | for (card = video_cards; card < video_cards_end; card++) { | ||
139 | mi = card->modes; | ||
140 | for (i = 0; i < card->nmodes; i++, mi++) { | ||
141 | if (mi->mode == mode) | ||
142 | return 1; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | /* Set mode (without recalc) */ | ||
150 | static int raw_set_mode(u16 mode, u16 *real_mode) | ||
151 | { | ||
152 | int nmode, i; | ||
153 | struct card_info *card; | ||
154 | struct mode_info *mi; | ||
155 | |||
156 | /* Drop the recalc bit if set */ | ||
157 | mode &= ~VIDEO_RECALC; | ||
158 | |||
159 | /* Scan for mode based on fixed ID, position, or resolution */ | ||
160 | nmode = 0; | ||
161 | for (card = video_cards; card < video_cards_end; card++) { | ||
162 | mi = card->modes; | ||
163 | for (i = 0; i < card->nmodes; i++, mi++) { | ||
164 | int visible = mi->x || mi->y; | ||
165 | |||
166 | if ((mode == nmode && visible) || | ||
167 | mode == mi->mode || | ||
168 | mode == (mi->y << 8)+mi->x) { | ||
169 | *real_mode = mi->mode; | ||
170 | return card->set_mode(mi); | ||
171 | } | ||
172 | |||
173 | if (visible) | ||
174 | nmode++; | ||
175 | } | ||
176 | } | ||
177 | |||
178 | /* Nothing found? Is it an "exceptional" (unprobed) mode? */ | ||
179 | for (card = video_cards; card < video_cards_end; card++) { | ||
180 | if (mode >= card->xmode_first && | ||
181 | mode < card->xmode_first+card->xmode_n) { | ||
182 | struct mode_info mix; | ||
183 | *real_mode = mix.mode = mode; | ||
184 | mix.x = mix.y = 0; | ||
185 | return card->set_mode(&mix); | ||
186 | } | ||
187 | } | ||
188 | |||
189 | /* Otherwise, failure... */ | ||
190 | return -1; | ||
191 | } | ||
192 | |||
193 | /* | ||
194 | * Recalculate the vertical video cutoff (hack!) | ||
195 | */ | ||
196 | static void vga_recalc_vertical(void) | ||
197 | { | ||
198 | unsigned int font_size, rows; | ||
199 | u16 crtc; | ||
200 | u8 pt, ov; | ||
201 | |||
202 | set_fs(0); | ||
203 | font_size = rdfs8(0x485); /* BIOS: font size (pixels) */ | ||
204 | rows = force_y ? force_y : rdfs8(0x484)+1; /* Text rows */ | ||
205 | |||
206 | rows *= font_size; /* Visible scan lines */ | ||
207 | rows--; /* ... minus one */ | ||
208 | |||
209 | crtc = vga_crtc(); | ||
210 | |||
211 | pt = in_idx(crtc, 0x11); | ||
212 | pt &= ~0x80; /* Unlock CR0-7 */ | ||
213 | out_idx(pt, crtc, 0x11); | ||
214 | |||
215 | out_idx((u8)rows, crtc, 0x12); /* Lower height register */ | ||
216 | |||
217 | ov = in_idx(crtc, 0x07); /* Overflow register */ | ||
218 | ov &= 0xbd; | ||
219 | ov |= (rows >> (8-1)) & 0x02; | ||
220 | ov |= (rows >> (9-6)) & 0x40; | ||
221 | out_idx(ov, crtc, 0x07); | ||
222 | } | ||
223 | |||
224 | /* Set mode (with recalc if specified) */ | ||
225 | static int set_mode(u16 mode) | ||
226 | { | ||
227 | int rv; | ||
228 | u16 real_mode; | ||
229 | |||
230 | /* Very special mode numbers... */ | ||
231 | if (mode == VIDEO_CURRENT_MODE) | ||
232 | return 0; /* Nothing to do... */ | ||
233 | else if (mode == NORMAL_VGA) | ||
234 | mode = VIDEO_80x25; | ||
235 | else if (mode == EXTENDED_VGA) | ||
236 | mode = VIDEO_8POINT; | ||
237 | |||
238 | rv = raw_set_mode(mode, &real_mode); | ||
239 | if (rv) | ||
240 | return rv; | ||
241 | |||
242 | if (mode & VIDEO_RECALC) | ||
243 | vga_recalc_vertical(); | ||
244 | |||
245 | /* Save the canonical mode number for the kernel, not | ||
246 | an alias, size specification or menu position */ | ||
247 | boot_params.hdr.vid_mode = real_mode; | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | static unsigned int get_entry(void) | 95 | static unsigned int get_entry(void) |
252 | { | 96 | { |
253 | char entry_buf[4]; | 97 | char entry_buf[4]; |
@@ -486,6 +330,7 @@ void set_video(void) | |||
486 | printf("Undefined video mode number: %x\n", mode); | 330 | printf("Undefined video mode number: %x\n", mode); |
487 | mode = ASK_VGA; | 331 | mode = ASK_VGA; |
488 | } | 332 | } |
333 | boot_params.hdr.vid_mode = mode; | ||
489 | vesa_store_edid(); | 334 | vesa_store_edid(); |
490 | store_mode_params(); | 335 | store_mode_params(); |
491 | 336 | ||
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 | ||
diff --git a/arch/x86/kernel/e820_64.c b/arch/x86/kernel/e820_64.c index a720f3d5ed9d..7f6c0c85c8f6 100644 --- a/arch/x86/kernel/e820_64.c +++ b/arch/x86/kernel/e820_64.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <asm/setup.h> | 27 | #include <asm/setup.h> |
28 | #include <asm/sections.h> | 28 | #include <asm/sections.h> |
29 | #include <asm/kdebug.h> | 29 | #include <asm/kdebug.h> |
30 | #include <asm/trampoline.h> | ||
30 | 31 | ||
31 | struct e820map e820; | 32 | struct e820map e820; |
32 | 33 | ||
@@ -58,8 +59,8 @@ struct early_res { | |||
58 | }; | 59 | }; |
59 | static struct early_res early_res[MAX_EARLY_RES] __initdata = { | 60 | static struct early_res early_res[MAX_EARLY_RES] __initdata = { |
60 | { 0, PAGE_SIZE, "BIOS data page" }, /* BIOS data page */ | 61 | { 0, PAGE_SIZE, "BIOS data page" }, /* BIOS data page */ |
61 | #ifdef CONFIG_SMP | 62 | #ifdef CONFIG_X86_TRAMPOLINE |
62 | { SMP_TRAMPOLINE_BASE, SMP_TRAMPOLINE_BASE + 2*PAGE_SIZE, "SMP_TRAMPOLINE" }, | 63 | { TRAMPOLINE_BASE, TRAMPOLINE_BASE + 2 * PAGE_SIZE, "TRAMPOLINE" }, |
63 | #endif | 64 | #endif |
64 | {} | 65 | {} |
65 | }; | 66 | }; |
diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S index c1d7a877d814..10a1955bb1d1 100644 --- a/arch/x86/kernel/head_64.S +++ b/arch/x86/kernel/head_64.S | |||
@@ -132,10 +132,6 @@ ident_complete: | |||
132 | addq %rbp, trampoline_level4_pgt + 0(%rip) | 132 | addq %rbp, trampoline_level4_pgt + 0(%rip) |
133 | addq %rbp, trampoline_level4_pgt + (511*8)(%rip) | 133 | addq %rbp, trampoline_level4_pgt + (511*8)(%rip) |
134 | #endif | 134 | #endif |
135 | #ifdef CONFIG_ACPI_SLEEP | ||
136 | addq %rbp, wakeup_level4_pgt + 0(%rip) | ||
137 | addq %rbp, wakeup_level4_pgt + (511*8)(%rip) | ||
138 | #endif | ||
139 | 135 | ||
140 | /* Due to ENTRY(), sometimes the empty space gets filled with | 136 | /* Due to ENTRY(), sometimes the empty space gets filled with |
141 | * zeros. Better take a jmp than relying on empty space being | 137 | * zeros. Better take a jmp than relying on empty space being |
diff --git a/arch/x86/kernel/setup_32.c b/arch/x86/kernel/setup_32.c index 4b198d9d0de3..5b0bffb7fcc9 100644 --- a/arch/x86/kernel/setup_32.c +++ b/arch/x86/kernel/setup_32.c | |||
@@ -192,7 +192,7 @@ EXPORT_SYMBOL(ist_info); | |||
192 | extern void early_cpu_init(void); | 192 | extern void early_cpu_init(void); |
193 | extern int root_mountflags; | 193 | extern int root_mountflags; |
194 | 194 | ||
195 | unsigned long saved_videomode; | 195 | unsigned long saved_video_mode; |
196 | 196 | ||
197 | #define RAMDISK_IMAGE_START_MASK 0x07FF | 197 | #define RAMDISK_IMAGE_START_MASK 0x07FF |
198 | #define RAMDISK_PROMPT_FLAG 0x8000 | 198 | #define RAMDISK_PROMPT_FLAG 0x8000 |
@@ -763,7 +763,7 @@ void __init setup_arch(char **cmdline_p) | |||
763 | edid_info = boot_params.edid_info; | 763 | edid_info = boot_params.edid_info; |
764 | apm_info.bios = boot_params.apm_bios_info; | 764 | apm_info.bios = boot_params.apm_bios_info; |
765 | ist_info = boot_params.ist_info; | 765 | ist_info = boot_params.ist_info; |
766 | saved_videomode = boot_params.hdr.vid_mode; | 766 | saved_video_mode = boot_params.hdr.vid_mode; |
767 | if( boot_params.sys_desc_table.length != 0 ) { | 767 | if( boot_params.sys_desc_table.length != 0 ) { |
768 | set_mca_bus(boot_params.sys_desc_table.table[3] & 0x2); | 768 | set_mca_bus(boot_params.sys_desc_table.table[3] & 0x2); |
769 | machine_id = boot_params.sys_desc_table.table[0]; | 769 | machine_id = boot_params.sys_desc_table.table[0]; |
diff --git a/arch/x86/kernel/setup_64.c b/arch/x86/kernel/setup_64.c index b80300710c08..674ef3510cdf 100644 --- a/arch/x86/kernel/setup_64.c +++ b/arch/x86/kernel/setup_64.c | |||
@@ -65,6 +65,7 @@ | |||
65 | #include <asm/mce.h> | 65 | #include <asm/mce.h> |
66 | #include <asm/ds.h> | 66 | #include <asm/ds.h> |
67 | #include <asm/topology.h> | 67 | #include <asm/topology.h> |
68 | #include <asm/trampoline.h> | ||
68 | 69 | ||
69 | #include <mach_apic.h> | 70 | #include <mach_apic.h> |
70 | #ifdef CONFIG_PARAVIRT | 71 | #ifdef CONFIG_PARAVIRT |
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index ca3929b16049..424600e671bd 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c | |||
@@ -53,6 +53,7 @@ | |||
53 | #include <asm/nmi.h> | 53 | #include <asm/nmi.h> |
54 | #include <asm/irq.h> | 54 | #include <asm/irq.h> |
55 | #include <asm/smp.h> | 55 | #include <asm/smp.h> |
56 | #include <asm/trampoline.h> | ||
56 | #include <asm/cpu.h> | 57 | #include <asm/cpu.h> |
57 | #include <asm/numa.h> | 58 | #include <asm/numa.h> |
58 | #include <asm/pgtable.h> | 59 | #include <asm/pgtable.h> |
@@ -140,7 +141,7 @@ static atomic_t init_deasserted; | |||
140 | static int boot_cpu_logical_apicid; | 141 | static int boot_cpu_logical_apicid; |
141 | 142 | ||
142 | /* ready for x86_64, no harm for x86, since it will overwrite after alloc */ | 143 | /* ready for x86_64, no harm for x86, since it will overwrite after alloc */ |
143 | unsigned char *trampoline_base = __va(SMP_TRAMPOLINE_BASE); | 144 | unsigned char *trampoline_base = __va(TRAMPOLINE_BASE); |
144 | 145 | ||
145 | /* representing cpus for which sibling maps can be computed */ | 146 | /* representing cpus for which sibling maps can be computed */ |
146 | static cpumask_t cpu_sibling_setup_map; | 147 | static cpumask_t cpu_sibling_setup_map; |
@@ -554,8 +555,7 @@ cpumask_t cpu_coregroup_map(int cpu) | |||
554 | * bootstrap into the page concerned. The caller | 555 | * bootstrap into the page concerned. The caller |
555 | * has made sure it's suitably aligned. | 556 | * has made sure it's suitably aligned. |
556 | */ | 557 | */ |
557 | 558 | unsigned long setup_trampoline(void) | |
558 | unsigned long __cpuinit setup_trampoline(void) | ||
559 | { | 559 | { |
560 | memcpy(trampoline_base, trampoline_data, | 560 | memcpy(trampoline_base, trampoline_data, |
561 | trampoline_end - trampoline_data); | 561 | trampoline_end - trampoline_data); |
diff --git a/arch/x86/kernel/trampoline_64.S b/arch/x86/kernel/trampoline_64.S index 2a07e67d6697..894293c598db 100644 --- a/arch/x86/kernel/trampoline_64.S +++ b/arch/x86/kernel/trampoline_64.S | |||
@@ -30,12 +30,7 @@ | |||
30 | #include <asm/msr.h> | 30 | #include <asm/msr.h> |
31 | #include <asm/segment.h> | 31 | #include <asm/segment.h> |
32 | 32 | ||
33 | /* We can free up trampoline after bootup if cpu hotplug is not supported. */ | ||
34 | #ifndef CONFIG_HOTPLUG_CPU | ||
35 | .section .cpuinit.data, "aw", @progbits | ||
36 | #else | ||
37 | .section .rodata, "a", @progbits | 33 | .section .rodata, "a", @progbits |
38 | #endif | ||
39 | 34 | ||
40 | .code16 | 35 | .code16 |
41 | 36 | ||
diff --git a/arch/x86/mach-voyager/voyager_smp.c b/arch/x86/mach-voyager/voyager_smp.c index 4397235c2e30..be7235bf105d 100644 --- a/arch/x86/mach-voyager/voyager_smp.c +++ b/arch/x86/mach-voyager/voyager_smp.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <asm/pgalloc.h> | 27 | #include <asm/pgalloc.h> |
28 | #include <asm/tlbflush.h> | 28 | #include <asm/tlbflush.h> |
29 | #include <asm/arch_hooks.h> | 29 | #include <asm/arch_hooks.h> |
30 | #include <asm/trampoline.h> | ||
30 | 31 | ||
31 | /* TLB state -- visible externally, indexed physically */ | 32 | /* TLB state -- visible externally, indexed physically */ |
32 | DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate) = { &init_mm, 0 }; | 33 | DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate) = { &init_mm, 0 }; |
diff --git a/include/asm-x86/smp.h b/include/asm-x86/smp.h index 3496e1c299b2..62ebdec394b9 100644 --- a/include/asm-x86/smp.h +++ b/include/asm-x86/smp.h | |||
@@ -45,22 +45,12 @@ DECLARE_PER_CPU(u16, cpu_llc_id); | |||
45 | DECLARE_PER_CPU(u16, x86_cpu_to_apicid); | 45 | DECLARE_PER_CPU(u16, x86_cpu_to_apicid); |
46 | DECLARE_PER_CPU(u16, x86_bios_cpu_apicid); | 46 | DECLARE_PER_CPU(u16, x86_bios_cpu_apicid); |
47 | 47 | ||
48 | /* | ||
49 | * Trampoline 80x86 program as an array. | ||
50 | */ | ||
51 | extern const unsigned char trampoline_data []; | ||
52 | extern const unsigned char trampoline_end []; | ||
53 | extern unsigned char *trampoline_base; | ||
54 | |||
55 | /* Static state in head.S used to set up a CPU */ | 48 | /* Static state in head.S used to set up a CPU */ |
56 | extern struct { | 49 | extern struct { |
57 | void *sp; | 50 | void *sp; |
58 | unsigned short ss; | 51 | unsigned short ss; |
59 | } stack_start; | 52 | } stack_start; |
60 | 53 | ||
61 | extern unsigned long init_rsp; | ||
62 | extern unsigned long initial_code; | ||
63 | |||
64 | struct smp_ops { | 54 | struct smp_ops { |
65 | void (*smp_prepare_boot_cpu)(void); | 55 | void (*smp_prepare_boot_cpu)(void); |
66 | void (*smp_prepare_cpus)(unsigned max_cpus); | 56 | void (*smp_prepare_cpus)(unsigned max_cpus); |
@@ -130,9 +120,6 @@ extern void __cpu_die(unsigned int cpu); | |||
130 | 120 | ||
131 | extern void prefill_possible_map(void); | 121 | extern void prefill_possible_map(void); |
132 | 122 | ||
133 | #define SMP_TRAMPOLINE_BASE 0x6000 | ||
134 | extern unsigned long setup_trampoline(void); | ||
135 | |||
136 | void smp_store_cpu_info(int id); | 123 | void smp_store_cpu_info(int id); |
137 | #define cpu_physical_id(cpu) per_cpu(x86_cpu_to_apicid, cpu) | 124 | #define cpu_physical_id(cpu) per_cpu(x86_cpu_to_apicid, cpu) |
138 | 125 | ||
diff --git a/include/asm-x86/trampoline.h b/include/asm-x86/trampoline.h new file mode 100644 index 000000000000..b156b08d0131 --- /dev/null +++ b/include/asm-x86/trampoline.h | |||
@@ -0,0 +1,21 @@ | |||
1 | #ifndef __TRAMPOLINE_HEADER | ||
2 | #define __TRAMPOLINE_HEADER | ||
3 | |||
4 | #ifndef __ASSEMBLY__ | ||
5 | |||
6 | /* | ||
7 | * Trampoline 80x86 program as an array. | ||
8 | */ | ||
9 | extern const unsigned char trampoline_data []; | ||
10 | extern const unsigned char trampoline_end []; | ||
11 | extern unsigned char *trampoline_base; | ||
12 | |||
13 | extern unsigned long init_rsp; | ||
14 | extern unsigned long initial_code; | ||
15 | |||
16 | #define TRAMPOLINE_BASE 0x6000 | ||
17 | extern unsigned long setup_trampoline(void); | ||
18 | |||
19 | #endif /* __ASSEMBLY__ */ | ||
20 | |||
21 | #endif /* __TRAMPOLINE_HEADER */ | ||