blob: 37dec7dab5aa273c376e47efddcdef6c0bd42929 [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
22 //reboot.c from linux
23/*this file mostly copied from Rudolf's S3 patch, some changes in acpi_jump_wake()*/
24#include <stdint.h>
25#include <string.h>
26#include <arch/io.h>
27#include <console/console.h>
28#include <delay.h>
29#include <part/init_timer.h>//for jaon_tsc_count_end
30#include "wakeup.h"
31
32
33
34
35int enable_a20(void);
36
37/* The following code and data reboots the machine by switching to real
38 mode and jumping to the BIOS reset entry point, as if the CPU has
39 really been reset. The previous version asked the keyboard
40 controller to pulse the CPU reset line, which is more thorough, but
41 doesn't work with at least one type of 486 motherboard. It is easy
42 to stop this code working; hence the copious comments. */
43
44static unsigned long long
45real_mode_gdt_entries [3] =
46{
47 0x0000000000000000ULL, /* Null descriptor */
48 0x00009a000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */
49 0x000092000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */
50};
51
52struct Xgt_desc_struct {
53 unsigned short size;
54 unsigned long address __attribute__((packed));
55 unsigned short pad;
56 } __attribute__ ((packed));
57
58static struct Xgt_desc_struct
59real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, (long)real_mode_gdt_entries },
60real_mode_idt = { 0x3ff, 0 },
61no_idt = { 0, 0 };
62
63
64/* This is 16-bit protected mode code to disable paging and the cache,
65 switch to real mode and jump to the BIOS reset code.
66
67 The instruction that switches to real mode by writing to CR0 must be
68 followed immediately by a far jump instruction, which set CS to a
69 valid value for real mode, and flushes the prefetch queue to avoid
70 running instructions that have already been decoded in protected
71 mode.
72
73 Clears all the flags except ET, especially PG (paging), PE
74 (protected-mode enable) and TS (task switch for coprocessor state
75 save). Flushes the TLB after paging has been disabled. Sets CD and
76 NW, to disable the cache on a 486, and invalidates the cache. This
77 is more like the state of a 486 after reset. I don't know if
78 something else should be done for other chips.
79
80 More could be done here to set up the registers as if a CPU reset had
81 occurred; hopefully real BIOSs don't assume much. */
82
83
84// 0x66, 0x0d, 0x00, 0x00, 0x00, 0x60, /* orl $0x60000000,%eax */
85
86static unsigned char real_mode_switch [] =
87{
88 0x66, 0x0f, 0x20, 0xc0, /* movl %cr0,%eax */
89 0x24, 0xfe, /* andb $0xfe,al */
90 0x66, 0x0f, 0x22, 0xc0 /* movl %eax,%cr0 */
91};
92static unsigned char jump_to_wakeup [] =
93{
94 0xea, 0x00, 0x00, 0x00, 0xe0 /* ljmp $0xffff,$0x0000 */
95};
96
97/*
98 * Switch to real mode and then execute the code
99 * specified by the code and length parameters.
100 * We assume that length will aways be less that 100!
101 */
102static unsigned char show31 [6] =
103{
104 0xb0, 0x31, 0xe6, 0x80, 0xeb ,0xFA /* ljmp $0xffff,$0x0000 */
105};
106static unsigned char show32 [6] =
107{
108 0xb0, 0x32, 0xe6, 0x80, 0xeb ,0xFA /* ljmp $0xffff,$0x0000 */
109};
110void acpi_jump_wake(u32 vector)
111{
112u32 tmp;
113u16 tmpvector;
114u32 dwEip;
115u8 Data;
116struct Xgt_desc_struct * wake_thunk16_Xgt_desc;
117
118 printk_debug("IN ACPI JUMP WAKE TO %x\n", vector);
119 if (enable_a20())
120 die("failed to enable A20\n");
121 printk_debug("IN ACPI JUMP WAKE TO 3 %x\n", vector);
122
123 * ((u16 *) (jump_to_wakeup+3)) = (u16)(vector>>4);
124 printk_debug("%x %x %x %x %x\n", jump_to_wakeup[0], jump_to_wakeup[1], jump_to_wakeup[2], jump_to_wakeup[3],jump_to_wakeup[4]);
125
126 memcpy ((void *) (WAKE_THUNK16_ADDR - sizeof (real_mode_switch) - 100),
127 real_mode_switch, sizeof (real_mode_switch));
128 memcpy ((void *) (WAKE_THUNK16_ADDR - 100), jump_to_wakeup, sizeof(jump_to_wakeup));
129
130 jason_tsc_count();
131 printk_emerg("file '%s', line %d\n\n", __FILE__, __LINE__);
132 jason_tsc_count_end();
133
134
135 unsigned long long * real_mode_gdt_entries_at_eseg;
136 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;
137 real_mode_gdt_entries_at_eseg [0] = 0x0000000000000000ULL; /* Null descriptor */
138 real_mode_gdt_entries_at_eseg [1] = 0x000f9a000000ffffULL; /* 16-bit real-mode 1M code at 0x00000000 */
139 real_mode_gdt_entries_at_eseg [2] = 0x000f93000000ffffULL; /* 16-bit real-mode 1M data at 0x00000000 */
140
141 wake_thunk16_Xgt_desc=WAKE_THUNK16_XDTR;
142 wake_thunk16_Xgt_desc[0].size=sizeof (real_mode_gdt_entries) - 1;
143 wake_thunk16_Xgt_desc[0].address=(long)real_mode_gdt_entries_at_eseg;
144 wake_thunk16_Xgt_desc[1].size=0x3ff;
145 wake_thunk16_Xgt_desc[1].address=0;
146 wake_thunk16_Xgt_desc[2].size=0;
147 wake_thunk16_Xgt_desc[2].address=0;
148
149 /*added this code to get current value of EIP
150 */
151 __asm__ volatile (
152 "calll geip\n\t"
153 "geip: \n\t"
154 "popl %0\n\t"
155 :"=a"(dwEip)
156 );
157
158 unsigned char *dest;
159 unsigned char *src;
160 src= (unsigned char *)dwEip;
161 dest=WAKE_RECOVER1M_CODE;
162 u32 i;
163 for (i = 0; i < 0x200; i++)
164 dest[i] = src[i];
165
166 __asm__ __volatile__ ("ljmp $0x0010,%0"//08 error
167 :
168 : "i" ((void *) (WAKE_RECOVER1M_CODE+0x20)));
169
170 /*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");
203
204
205 __asm__ volatile (
206 /* set new esp, maybe ebp should not equal to esp?,
207 due to the variable in acpi_jump_wake?, anyway, this may be not a big problem.
208 and I didnt clear the area (ef000+-0x200) to zero.
209 */
210 "movl %0, %%ebp\n\t"
211 "movl %0, %%esp\n\t"
212 ::"a"(WAKE_THUNK16_STACK)
213 );
214
215
216 /* added this
217 only "src" and "dest" use the new stack, and the esp maybe also used in resumevector
218 */
219#if PAYLOAD_IS_SEABIOS==1
220 // WAKE_MEM_INFO inited in get_set_top_available_mem in tables.c
221 src = (unsigned char *)((* (u32*)WAKE_MEM_INFO)- 64*1024-0x100000);
222 dest = 0;
223 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.
224 dest[i] = src[i];
225 /*__asm__ volatile (
226 "movl %0, %%esi\n\t"
227 "movl $0, %%edi\n\t"
228 "movl $0xa0000, %%ecx\n\t"
229 "shrl $2, %%ecx\n\t"
230 "rep movsd\n\t"
231 ::"a"(src)
232 );*/
233 src = (unsigned char *)((* (u32*)WAKE_MEM_INFO)- 64*1024-0x100000+0xc0000);
234 //dest = 0xc0000;
235 //for (i = 0; i < 0x20000; i++)
236 // 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 );*/
245
246
247 src = (unsigned char *)((* (u32*)WAKE_MEM_INFO)- 64*1024-0x100000+0xe0000+WAKE_SPECIAL_SIZE);
248 //dest = 0xf0000;
249 //for (i = 0; i < 0x10000; i++)
250 // dest[i] = src[i];
251 __asm__ volatile (
252 "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"
257 ::"r"(src),"r"(0xe0000+WAKE_SPECIAL_SIZE), "r"(0x10000-WAKE_SPECIAL_SIZE)
258 );
259
260 src = (unsigned char *)((* (u32*)WAKE_MEM_INFO)- 64*1024-0x100000+0xf0000);
261 //dest = 0xf0000;
262 //for (i = 0; i < 0x10000; i++)
263 // dest[i] = src[i];
264 __asm__ volatile (
265 "movl %0, %%esi\n\t"
266 "movl $0xf0000, %%edi\n\t"
267 "movl $0x10000, %%ecx\n\t"
268 "shrl $2, %%ecx\n\t"
269 "rep movsd\n\t"
270 ::"a"(src)
271 );
272
273 asm volatile("wbinvd");
274#endif
275 /* Set up the IDT for real mode. */
276 asm volatile("lidt %0"::"m" (wake_thunk16_Xgt_desc[1]));
277
278 /* 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. */
281 asm volatile("lgdt %0"::"m" (wake_thunk16_Xgt_desc[0]));
282
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
289 __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"
294 "\tmovl %%eax,%%ss" : : : "eax");
295
296 /* Jump to the 16-bit code that we copied earlier. It disables paging
297 and the cache, switches to real mode, and jumps to the BIOS reset
298 entry point. */
299
300 __asm__ __volatile__ ("ljmp $0x0008,%0"
301 :
302 : "i" ((void *) (WAKE_THUNK16_ADDR - sizeof (real_mode_switch) - 100)));
303}
304
305
306/* -*- linux-c -*- ------------------------------------------------------- *
307 *
308 * Copyright (C) 1991, 1992 Linus Torvalds
309 * Copyright 2007 rPath, Inc. - All Rights Reserved
310 *
311 * This file is part of the Linux kernel, and is made available under
312 * the terms of the GNU General Public License version 2.
313 *
314 * ----------------------------------------------------------------------- */
315
316/*
317 * arch/i386/boot/a20.c
318 *
319 * Enable A20 gate (return -1 on failure)
320 */
321
322//#include "boot.h"
323
324#define MAX_8042_LOOPS 100000
325
326static int empty_8042(void)
327{
328 u8 status;
329 int loops = MAX_8042_LOOPS;
330
331 while (loops--) {
332 udelay(1);
333
334 status = inb(0x64);
335 if (status & 1) {
336 /* Read and discard input data */
337 udelay(1);
338 (void)inb(0x60);
339 } else if (!(status & 2)) {
340 /* Buffers empty, finished! */
341 return 0;
342 }
343 }
344
345 return -1;
346}
347
348/* Returns nonzero if the A20 line is enabled. The memory address
349 used as a test is the int $0x80 vector, which should be safe. */
350
351#define A20_TEST_ADDR (4*0x80)
352#define A20_TEST_SHORT 32
353#define A20_TEST_LONG 2097152 /* 2^21 */
354
355static int a20_test(int loops)
356{
357 int ok = 0;
358 int saved, ctr;
359
360// set_fs(0x0000);
361// set_gs(0xffff);
362
363 saved = ctr = *((u32*) A20_TEST_ADDR);
364
365 while (loops--) {
366 //wrfs32(++ctr, A20_TEST_ADDR);
367
368 *((u32*) A20_TEST_ADDR) = ++ctr;
369
370 udelay(1); /* Serialize and make delay constant */
371
372 ok = *((u32 *) A20_TEST_ADDR+0xffff0+0x10) ^ ctr;
373 if (ok)
374 break;
375 }
376
377 *((u32*) A20_TEST_ADDR) = saved;
378 return ok;
379}
380
381/* Quick test to see if A20 is already enabled */
382static int a20_test_short(void)
383{
384 return a20_test(A20_TEST_SHORT);
385}
386
387/* Longer test that actually waits for A20 to come on line; this
388 is useful when dealing with the KBC or other slow external circuitry. */
389static int a20_test_long(void)
390{
391 return a20_test(A20_TEST_LONG);
392}
393
394static void enable_a20_kbc(void)
395{
396 empty_8042();
397
398 outb(0xd1, 0x64); /* Command write */
399 empty_8042();
400
401 outb(0xdf, 0x60); /* A20 on */
402 empty_8042();
403}
404
405static void enable_a20_fast(void)
406{
407 u8 port_a;
408
409 port_a = inb(0x92); /* Configuration port A */
410 port_a |= 0x02; /* Enable A20 */
411 port_a &= ~0x01; /* Do not reset machine */
412 outb(port_a, 0x92);
413}
414
415/*
416 * Actual routine to enable A20; return 0 on ok, -1 on failure
417 */
418
419#define A20_ENABLE_LOOPS 255 /* Number of times to try */
420
421int enable_a20(void)
422{
423 int loops = A20_ENABLE_LOOPS;
424
425 while (loops--) {
426 /* First, check to see if A20 is already enabled
427 (legacy free, etc.) */
428 if (a20_test_short())
429 return 0;
430
431 /* Try enabling A20 through the keyboard controller */
432 empty_8042();
433//if (a20_test_short())
434// return 0; /* BIOS worked, but with delayed reaction */
435
436 enable_a20_kbc();
437 if (a20_test_long())
438 return 0;
439
440 /* Finally, try enabling the "fast A20 gate" */
441 enable_a20_fast();
442 if (a20_test_long())
443 return 0;
444 }
445
446 return -1;
447}