blob: a7aa6a38c3c00b842b9c08512598d1197ae49053 [file] [log] [blame]
Uwe Hermann20a98c92009-06-05 23:02:43 +00001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2008 Rudolf Marek <r.marek@assembler.cz>
5 * Copyright (C) 2009 One Laptop per Child, Association, Inc.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
Uwe Hermann0ffff342009-06-07 13:46:50 +000022/* reboot.c from Linux. */
23
24/*
25 * This file mostly copied from Rudolf's S3 patch, some changes in
26 * acpi_jump_wake().
27 */
28
Uwe Hermann20a98c92009-06-05 23:02:43 +000029#include <stdint.h>
30#include <string.h>
31#include <arch/io.h>
32#include <console/console.h>
33#include <delay.h>
Uwe Hermann0ffff342009-06-07 13:46:50 +000034#include <part/init_timer.h> /* for jason_tsc_count_end */
Uwe Hermann20a98c92009-06-05 23:02:43 +000035#include "wakeup.h"
36
Uwe Hermann20a98c92009-06-05 23:02:43 +000037int enable_a20(void);
38
Uwe Hermann0ffff342009-06-07 13:46:50 +000039/*
40 * The following code and data reboots the machine by switching to real
41 * mode and jumping to the BIOS reset entry point, as if the CPU has
42 * really been reset. The previous version asked the keyboard
43 * controller to pulse the CPU reset line, which is more thorough, but
44 * doesn't work with at least one type of 486 motherboard. It is easy
45 * to stop this code working; hence the copious comments.
46 */
Uwe Hermann20a98c92009-06-05 23:02:43 +000047
Uwe Hermann0ffff342009-06-07 13:46:50 +000048static unsigned long long real_mode_gdt_entries[3] = {
Uwe Hermann20a98c92009-06-05 23:02:43 +000049 0x0000000000000000ULL, /* Null descriptor */
50 0x00009a000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */
51 0x000092000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */
52};
53
54struct Xgt_desc_struct {
Uwe Hermann0ffff342009-06-07 13:46:50 +000055 unsigned short size;
56 unsigned long address __attribute__ ((packed));
57 unsigned short pad;
58} __attribute__ ((packed));
Uwe Hermann20a98c92009-06-05 23:02:43 +000059
60static struct Xgt_desc_struct
Uwe Hermann0ffff342009-06-07 13:46:50 +000061 real_mode_gdt =
62 { sizeof(real_mode_gdt_entries) - 1, (long)real_mode_gdt_entries },
63 real_mode_idt = {
640x3ff, 0}, no_idt = {
650, 0};
Uwe Hermann20a98c92009-06-05 23:02:43 +000066
Uwe Hermann0ffff342009-06-07 13:46:50 +000067/*
68 * This is 16-bit protected mode code to disable paging and the cache,
69 * switch to real mode and jump to the BIOS reset code.
70 *
71 * The instruction that switches to real mode by writing to CR0 must be
72 * followed immediately by a far jump instruction, which set CS to a
73 * valid value for real mode, and flushes the prefetch queue to avoid
74 * running instructions that have already been decoded in protected
75 * mode.
76 *
77 * Clears all the flags except ET, especially PG (paging), PE
78 * (protected-mode enable) and TS (task switch for coprocessor state
79 * save). Flushes the TLB after paging has been disabled. Sets CD and
80 * NW, to disable the cache on a 486, and invalidates the cache. This
81 * is more like the state of a 486 after reset. I don't know if
82 * something else should be done for other chips.
83 *
84 * More could be done here to set up the registers as if a CPU reset had
85 * occurred; hopefully real BIOSs don't assume much.
86 */
Uwe Hermann20a98c92009-06-05 23:02:43 +000087
Uwe Hermann0ffff342009-06-07 13:46:50 +000088// 0x66, 0x0d, 0x00, 0x00, 0x00, 0x60, /* orl $0x60000000,%eax */
Uwe Hermann20a98c92009-06-05 23:02:43 +000089
Uwe Hermann0ffff342009-06-07 13:46:50 +000090static unsigned char real_mode_switch[] = {
91 0x66, 0x0f, 0x20, 0xc0, /* movl %cr0,%eax */
92 0x24, 0xfe, /* andb $0xfe,al */
93 0x66, 0x0f, 0x22, 0xc0 /* movl %eax,%cr0 */
Uwe Hermann20a98c92009-06-05 23:02:43 +000094};
Uwe Hermann0ffff342009-06-07 13:46:50 +000095
96static unsigned char jump_to_wakeup[] = {
97 0xea, 0x00, 0x00, 0x00, 0xe0 /* ljmp $0xffff, $0x0000 */
Uwe Hermann20a98c92009-06-05 23:02:43 +000098};
99
100/*
101 * Switch to real mode and then execute the code
102 * specified by the code and length parameters.
103 * We assume that length will aways be less that 100!
Uwe Hermann0ffff342009-06-07 13:46:50 +0000104 */
105static unsigned char show31[6] = {
106 0xb0, 0x31, 0xe6, 0x80, 0xeb, 0xFA /* ljmp $0xffff,$0x0000 */
107};
108
109static unsigned char show32[6] = {
110 0xb0, 0x32, 0xe6, 0x80, 0xeb, 0xFA /* ljmp $0xffff,$0x0000 */
111};
112
Uwe Hermann20a98c92009-06-05 23:02:43 +0000113void acpi_jump_wake(u32 vector)
114{
Uwe Hermann0ffff342009-06-07 13:46:50 +0000115 u32 tmp;
116 u16 tmpvector;
117 u32 dwEip;
118 u8 Data;
119 struct Xgt_desc_struct *wake_thunk16_Xgt_desc;
Uwe Hermann20a98c92009-06-05 23:02:43 +0000120
121 printk_debug("IN ACPI JUMP WAKE TO %x\n", vector);
122 if (enable_a20())
123 die("failed to enable A20\n");
124 printk_debug("IN ACPI JUMP WAKE TO 3 %x\n", vector);
Uwe Hermann20a98c92009-06-05 23:02:43 +0000125
Uwe Hermann0ffff342009-06-07 13:46:50 +0000126 *((u16 *) (jump_to_wakeup + 3)) = (u16) (vector >> 4);
127 printk_debug("%x %x %x %x %x\n", jump_to_wakeup[0], jump_to_wakeup[1],
128 jump_to_wakeup[2], jump_to_wakeup[3], jump_to_wakeup[4]);
129
130 memcpy((void *)(WAKE_THUNK16_ADDR - sizeof(real_mode_switch) - 100),
131 real_mode_switch, sizeof(real_mode_switch));
132 memcpy((void *)(WAKE_THUNK16_ADDR - 100), jump_to_wakeup,
133 sizeof(jump_to_wakeup));
Uwe Hermann20a98c92009-06-05 23:02:43 +0000134
135 jason_tsc_count();
Uwe Hermann0ffff342009-06-07 13:46:50 +0000136 printk_emerg("file '%s', line %d\n\n", __FILE__, __LINE__);
Uwe Hermann20a98c92009-06-05 23:02:43 +0000137 jason_tsc_count_end();
138
Uwe Hermann0ffff342009-06-07 13:46:50 +0000139 unsigned long long *real_mode_gdt_entries_at_eseg;
140 real_mode_gdt_entries_at_eseg = WAKE_THUNK16_GDT; //copy from real_mode_gdt_entries and change limition to 1M and data base to 0;
141 real_mode_gdt_entries_at_eseg[0] = 0x0000000000000000ULL; /* Null descriptor */
142 real_mode_gdt_entries_at_eseg[1] = 0x000f9a000000ffffULL; /* 16-bit real-mode 1M code at 0x00000000 */
143 real_mode_gdt_entries_at_eseg[2] = 0x000f93000000ffffULL; /* 16-bit real-mode 1M data at 0x00000000 */
Uwe Hermann20a98c92009-06-05 23:02:43 +0000144
Uwe Hermann0ffff342009-06-07 13:46:50 +0000145 wake_thunk16_Xgt_desc = WAKE_THUNK16_XDTR;
146 wake_thunk16_Xgt_desc[0].size = sizeof(real_mode_gdt_entries) - 1;
147 wake_thunk16_Xgt_desc[0].address = (long)real_mode_gdt_entries_at_eseg;
148 wake_thunk16_Xgt_desc[1].size = 0x3ff;
149 wake_thunk16_Xgt_desc[1].address = 0;
150 wake_thunk16_Xgt_desc[2].size = 0;
151 wake_thunk16_Xgt_desc[2].address = 0;
Uwe Hermann20a98c92009-06-05 23:02:43 +0000152
Uwe Hermann0ffff342009-06-07 13:46:50 +0000153 /*added this code to get current value of EIP
154 */
155 __asm__ volatile ("calll geip\n\t"
156 "geip: \n\t" "popl %0\n\t":"=a" (dwEip)
157 );
Uwe Hermann20a98c92009-06-05 23:02:43 +0000158
159 unsigned char *dest;
160 unsigned char *src;
Uwe Hermann0ffff342009-06-07 13:46:50 +0000161 src = (unsigned char *)dwEip;
162 dest = WAKE_RECOVER1M_CODE;
Uwe Hermann20a98c92009-06-05 23:02:43 +0000163 u32 i;
Uwe Hermann0ffff342009-06-07 13:46:50 +0000164 for (i = 0; i < 0x200; i++)
165 dest[i] = src[i];
Uwe Hermann20a98c92009-06-05 23:02:43 +0000166
Uwe Hermann0ffff342009-06-07 13:46:50 +0000167 __asm__ __volatile__("ljmp $0x0010,%0" //08 error
168 ::"i"((void *)(WAKE_RECOVER1M_CODE + 0x20)));
Uwe Hermann20a98c92009-06-05 23:02:43 +0000169
Uwe Hermann0ffff342009-06-07 13:46:50 +0000170 /*added 0x20 "nop" to make sure the ljmp will not jump then halt */
171 asm volatile ("nop");
172 asm volatile ("nop");
173 asm volatile ("nop");
174 asm volatile ("nop");
175 asm volatile ("nop");
176 asm volatile ("nop");
177 asm volatile ("nop");
178 asm volatile ("nop");
179 asm volatile ("nop");
180 asm volatile ("nop");
181
182 asm volatile ("nop");
183 asm volatile ("nop");
184 asm volatile ("nop");
185 asm volatile ("nop");
186 asm volatile ("nop");
187 asm volatile ("nop");
188 asm volatile ("nop");
189 asm volatile ("nop");
190 asm volatile ("nop");
191 asm volatile ("nop");
192
193 asm volatile ("nop");
194 asm volatile ("nop");
195 asm volatile ("nop");
196 asm volatile ("nop");
197 asm volatile ("nop");
198 asm volatile ("nop");
199 asm volatile ("nop");
200 asm volatile ("nop");
201 asm volatile ("nop");
202 asm volatile ("nop");
Uwe Hermann20a98c92009-06-05 23:02:43 +0000203
204 __asm__ volatile (
Uwe Hermann0ffff342009-06-07 13:46:50 +0000205 /* set new esp, maybe ebp should not equal to esp?,
206 due to the variable in acpi_jump_wake?, anyway, this may be not a big problem.
207 and I didnt clear the area (ef000+-0x200) to zero.
208 */
209 "movl %0, %%ebp\n\t"
210 "movl %0, %%esp\n\t"::"a" (WAKE_THUNK16_STACK)
211 );
Uwe Hermann20a98c92009-06-05 23:02:43 +0000212
213 /* added this
Uwe Hermann0ffff342009-06-07 13:46:50 +0000214 only "src" and "dest" use the new stack, and the esp maybe also used in resumevector
215 */
216#if PAYLOAD_IS_SEABIOS==1
Uwe Hermann20a98c92009-06-05 23:02:43 +0000217 // WAKE_MEM_INFO inited in get_set_top_available_mem in tables.c
Uwe Hermann0ffff342009-06-07 13:46:50 +0000218 src =
219 (unsigned char *)((*(u32 *) WAKE_MEM_INFO) - 64 * 1024 - 0x100000);
220 dest = 0;
221 for (i = 0; i < 0xa0000; i++) //if recovered 0-e0000, then when resume, before winxp turn on the desktop screen ,there is gray background which last 1sec.
Uwe Hermann20a98c92009-06-05 23:02:43 +0000222 dest[i] = src[i];
223 /*__asm__ volatile (
224 "movl %0, %%esi\n\t"
225 "movl $0, %%edi\n\t"
226 "movl $0xa0000, %%ecx\n\t"
227 "shrl $2, %%ecx\n\t"
228 "rep movsd\n\t"
229 ::"a"(src)
230 );*/
Uwe Hermann0ffff342009-06-07 13:46:50 +0000231 src =
232 (unsigned char *)((*(u32 *) WAKE_MEM_INFO) - 64 * 1024 - 0x100000 +
233 0xc0000);
Uwe Hermann20a98c92009-06-05 23:02:43 +0000234 //dest = 0xc0000;
235 //for (i = 0; i < 0x20000; i++)
Uwe Hermann0ffff342009-06-07 13:46:50 +0000236 // dest[i] = src[i];
237 /* __asm__ volatile (
238 "movl %0, %%esi\n\t"
239 "movl $0xc0000, %%edi\n\t"
240 "movl $0x20000, %%ecx\n\t"
241 "shrl $2, %%ecx\n\t"
242 "rep movsd\n\t"
243 ::"a"(src)
244 ); */
Uwe Hermann20a98c92009-06-05 23:02:43 +0000245
Uwe Hermann0ffff342009-06-07 13:46:50 +0000246 src =
247 (unsigned char *)((*(u32 *) WAKE_MEM_INFO) - 64 * 1024 - 0x100000 +
248 0xe0000 + WAKE_SPECIAL_SIZE);
249 //dest = 0xf0000;
250 //for (i = 0; i < 0x10000; i++)
251 // dest[i] = src[i];
252 __asm__ volatile ("movl %0, %%esi\n\t"
253 "movl %1, %%edi\n\t"
254 "movl %2, %%ecx\n\t"
255 "shrl $2, %%ecx\n\t"
256 "rep movsd\n\t"::"r" (src),
257 "r"(0xe0000 + WAKE_SPECIAL_SIZE),
258 "r"(0x10000 - WAKE_SPECIAL_SIZE)
259 );
Uwe Hermann20a98c92009-06-05 23:02:43 +0000260
Uwe Hermann0ffff342009-06-07 13:46:50 +0000261 src =
262 (unsigned char *)((*(u32 *) WAKE_MEM_INFO) - 64 * 1024 - 0x100000 +
263 0xf0000);
264 //dest = 0xf0000;
265 //for (i = 0; i < 0x10000; i++)
266 // dest[i] = src[i];
267 __asm__ volatile ("movl %0, %%esi\n\t"
268 "movl $0xf0000, %%edi\n\t"
269 "movl $0x10000, %%ecx\n\t"
270 "shrl $2, %%ecx\n\t" "rep movsd\n\t"::"a" (src)
271 );
272
273 asm volatile ("wbinvd");
Uwe Hermann20a98c92009-06-05 23:02:43 +0000274#endif
275 /* Set up the IDT for real mode. */
Uwe Hermann0ffff342009-06-07 13:46:50 +0000276 asm volatile ("lidt %0"::"m" (wake_thunk16_Xgt_desc[1]));
277
Uwe Hermann20a98c92009-06-05 23:02:43 +0000278 /* Set up a GDT from which we can load segment descriptors for real
279 mode. The GDT is not used in real mode; it is just needed here to
280 prepare the descriptors. */
Uwe Hermann0ffff342009-06-07 13:46:50 +0000281 asm volatile ("lgdt %0"::"m" (wake_thunk16_Xgt_desc[0]));
Uwe Hermann20a98c92009-06-05 23:02:43 +0000282
283 /* Load the data segment registers, and thus the descriptors ready for
284 real mode. The base address of each segment is 0x100, 16 times the
285 selector value being loaded here. This is so that the segment
286 registers don't have to be reloaded after switching to real mode:
287 the values are consistent for real mode operation already. */
288
Uwe Hermann0ffff342009-06-07 13:46:50 +0000289 __asm__ __volatile__("movl $0x0010,%%eax\n"
290 "\tmovl %%eax,%%ds\n"
291 "\tmovl %%eax,%%es\n"
292 "\tmovl %%eax,%%fs\n"
293 "\tmovl %%eax,%%gs\n" "\tmovl %%eax,%%ss":::"eax");
Uwe Hermann20a98c92009-06-05 23:02:43 +0000294
295 /* Jump to the 16-bit code that we copied earlier. It disables paging
296 and the cache, switches to real mode, and jumps to the BIOS reset
297 entry point. */
Uwe Hermann20a98c92009-06-05 23:02:43 +0000298
Uwe Hermann0ffff342009-06-07 13:46:50 +0000299 __asm__
300 __volatile__("ljmp $0x0008,%0"::"i"
301 ((void *)(WAKE_THUNK16_ADDR -
302 sizeof(real_mode_switch) - 100)));
303}
Uwe Hermann20a98c92009-06-05 23:02:43 +0000304
305/* -*- linux-c -*- ------------------------------------------------------- *
306 *
307 * Copyright (C) 1991, 1992 Linus Torvalds
308 * Copyright 2007 rPath, Inc. - All Rights Reserved
309 *
310 * This file is part of the Linux kernel, and is made available under
311 * the terms of the GNU General Public License version 2.
312 *
313 * ----------------------------------------------------------------------- */
314
315/*
316 * arch/i386/boot/a20.c
317 *
318 * Enable A20 gate (return -1 on failure)
319 */
320
321//#include "boot.h"
322
323#define MAX_8042_LOOPS 100000
324
325static int empty_8042(void)
326{
327 u8 status;
328 int loops = MAX_8042_LOOPS;
329
330 while (loops--) {
331 udelay(1);
332
333 status = inb(0x64);
334 if (status & 1) {
335 /* Read and discard input data */
336 udelay(1);
337 (void)inb(0x60);
338 } else if (!(status & 2)) {
339 /* Buffers empty, finished! */
340 return 0;
341 }
342 }
343
344 return -1;
345}
346
347/* Returns nonzero if the A20 line is enabled. The memory address
348 used as a test is the int $0x80 vector, which should be safe. */
349
350#define A20_TEST_ADDR (4*0x80)
351#define A20_TEST_SHORT 32
352#define A20_TEST_LONG 2097152 /* 2^21 */
353
354static int a20_test(int loops)
355{
356 int ok = 0;
357 int saved, ctr;
358
Uwe Hermann0ffff342009-06-07 13:46:50 +0000359// set_fs(0x0000);
360// set_gs(0xffff);
Uwe Hermann20a98c92009-06-05 23:02:43 +0000361
Uwe Hermann0ffff342009-06-07 13:46:50 +0000362 saved = ctr = *((u32 *) A20_TEST_ADDR);
Uwe Hermann20a98c92009-06-05 23:02:43 +0000363
364 while (loops--) {
365 //wrfs32(++ctr, A20_TEST_ADDR);
Uwe Hermann0ffff342009-06-07 13:46:50 +0000366
367 *((u32 *) A20_TEST_ADDR) = ++ctr;
368
Uwe Hermann20a98c92009-06-05 23:02:43 +0000369 udelay(1); /* Serialize and make delay constant */
Uwe Hermann0ffff342009-06-07 13:46:50 +0000370
371 ok = *((u32 *) A20_TEST_ADDR + 0xffff0 + 0x10) ^ ctr;
Uwe Hermann20a98c92009-06-05 23:02:43 +0000372 if (ok)
373 break;
374 }
375
Uwe Hermann0ffff342009-06-07 13:46:50 +0000376 *((u32 *) A20_TEST_ADDR) = saved;
Uwe Hermann20a98c92009-06-05 23:02:43 +0000377 return ok;
378}
379
380/* Quick test to see if A20 is already enabled */
381static int a20_test_short(void)
382{
383 return a20_test(A20_TEST_SHORT);
384}
385
386/* Longer test that actually waits for A20 to come on line; this
387 is useful when dealing with the KBC or other slow external circuitry. */
388static int a20_test_long(void)
389{
390 return a20_test(A20_TEST_LONG);
391}
392
393static void enable_a20_kbc(void)
394{
395 empty_8042();
396
397 outb(0xd1, 0x64); /* Command write */
398 empty_8042();
399
400 outb(0xdf, 0x60); /* A20 on */
401 empty_8042();
402}
403
404static void enable_a20_fast(void)
405{
406 u8 port_a;
407
408 port_a = inb(0x92); /* Configuration port A */
Uwe Hermann0ffff342009-06-07 13:46:50 +0000409 port_a |= 0x02; /* Enable A20 */
Uwe Hermann20a98c92009-06-05 23:02:43 +0000410 port_a &= ~0x01; /* Do not reset machine */
411 outb(port_a, 0x92);
412}
413
414/*
415 * Actual routine to enable A20; return 0 on ok, -1 on failure
416 */
417
418#define A20_ENABLE_LOOPS 255 /* Number of times to try */
419
420int enable_a20(void)
421{
422 int loops = A20_ENABLE_LOOPS;
423
424 while (loops--) {
425 /* First, check to see if A20 is already enabled
426 (legacy free, etc.) */
427 if (a20_test_short())
428 return 0;
429
430 /* Try enabling A20 through the keyboard controller */
431 empty_8042();
432//if (a20_test_short())
Uwe Hermann0ffff342009-06-07 13:46:50 +0000433// return 0; /* BIOS worked, but with delayed reaction */
Uwe Hermann20a98c92009-06-05 23:02:43 +0000434
435 enable_a20_kbc();
436 if (a20_test_long())
437 return 0;
438
439 /* Finally, try enabling the "fast A20 gate" */
440 enable_a20_fast();
441 if (a20_test_long())
442 return 0;
443 }
444
445 return -1;
446}