2 * This file is part of the coreboot project.
4 * Copyright (C) 2008 Rudolf Marek <r.marek@assembler.cz>
5 * Copyright (C) 2009 One Laptop per Child, Association, Inc.
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.
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.
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
22 /* Parts of this code is taken from reboot.c from Linux. */
25 * This file mostly copied from Rudolf's S3 patch, some changes in
32 #include <console/console.h>
39 * The following code and data reboots the machine by switching to real
40 * mode and jumping to the BIOS reset entry point, as if the CPU has
41 * really been reset. The previous version asked the keyboard
42 * controller to pulse the CPU reset line, which is more thorough, but
43 * doesn't work with at least one type of 486 motherboard. It is easy
44 * to stop this code working; hence the copious comments.
47 static unsigned long long real_mode_gdt_entries[3] = {
48 0x0000000000000000ULL, /* Null descriptor */
49 0x00009a000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */
50 0x000092000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */
53 struct Xgt_desc_struct {
55 unsigned long address __attribute__ ((packed));
57 } __attribute__ ((packed));
59 static struct Xgt_desc_struct real_mode_gdt = {
60 sizeof(real_mode_gdt_entries) - 1,
61 (long)real_mode_gdt_entries
63 real_mode_idt = {0x3ff, 0},
67 * This is 16-bit protected mode code to disable paging and the cache,
68 * switch to real mode and jump to the BIOS reset code.
70 * The instruction that switches to real mode by writing to CR0 must be
71 * followed immediately by a far jump instruction, which set CS to a
72 * valid value for real mode, and flushes the prefetch queue to avoid
73 * running instructions that have already been decoded in protected
76 * Clears all the flags except ET, especially PG (paging), PE
77 * (protected-mode enable) and TS (task switch for coprocessor state
78 * save). Flushes the TLB after paging has been disabled. Sets CD and
79 * NW, to disable the cache on a 486, and invalidates the cache. This
80 * is more like the state of a 486 after reset. I don't know if
81 * something else should be done for other chips.
83 * More could be done here to set up the registers as if a CPU reset had
84 * occurred; hopefully real BIOSs don't assume much.
87 // 0x66, 0x0d, 0x00, 0x00, 0x00, 0x60, /* orl $0x60000000, %eax */
89 static unsigned char real_mode_switch[] = {
90 0x66, 0x0f, 0x20, 0xc0, /* movl %cr0,%eax */
91 0x24, 0xfe, /* andb $0xfe,al */
92 0x66, 0x0f, 0x22, 0xc0 /* movl %eax,%cr0 */
95 static unsigned char jump_to_wakeup[] = {
96 0xea, 0x00, 0x00, 0x00, 0xe0 /* ljmp $0xffff, $0x0000 */
100 * Switch to real mode and then execute the code
101 * specified by the code and length parameters.
102 * We assume that length will aways be less that 100!
104 static unsigned char show31[6] = {
105 0xb0, 0x31, 0xe6, 0x80, 0xeb, 0xFA /* ljmp $0xffff, $0x0000 */
108 static unsigned char show32[6] = {
109 0xb0, 0x32, 0xe6, 0x80, 0xeb, 0xFA /* ljmp $0xffff, $0x0000 */
112 void acpi_jump_wake(u32 vector)
117 struct Xgt_desc_struct *wake_thunk16_Xgt_desc;
119 printk(BIOS_DEBUG, "IN ACPI JUMP WAKE TO %x\n", vector);
121 die("failed to enable A20\n");
122 printk(BIOS_DEBUG, "IN ACPI JUMP WAKE TO 3 %x\n", vector);
124 *((u16 *) (jump_to_wakeup + 3)) = (u16) (vector >> 4);
125 printk(BIOS_DEBUG, "%x %x %x %x %x\n", jump_to_wakeup[0], jump_to_wakeup[1],
126 jump_to_wakeup[2], jump_to_wakeup[3], jump_to_wakeup[4]);
128 memcpy((void *)(WAKE_THUNK16_ADDR - sizeof(real_mode_switch) - 100),
129 real_mode_switch, sizeof(real_mode_switch));
130 memcpy((void *)(WAKE_THUNK16_ADDR - 100), jump_to_wakeup,
131 sizeof(jump_to_wakeup));
134 printk(BIOS_EMERG, "file '%s', line %d\n\n", __FILE__, __LINE__);
135 //jason_tsc_count_end();
137 unsigned long long *real_mode_gdt_entries_at_eseg;
138 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; */
139 real_mode_gdt_entries_at_eseg[0] = 0x0000000000000000ULL; /* Null descriptor */
140 real_mode_gdt_entries_at_eseg[1] = 0x000f9a000000ffffULL; /* 16-bit real-mode 1M code at 0x00000000 */
141 real_mode_gdt_entries_at_eseg[2] = 0x000f93000000ffffULL; /* 16-bit real-mode 1M data at 0x00000000 */
143 wake_thunk16_Xgt_desc = WAKE_THUNK16_XDTR;
144 wake_thunk16_Xgt_desc[0].size = sizeof(real_mode_gdt_entries) - 1;
145 wake_thunk16_Xgt_desc[0].address = (long)real_mode_gdt_entries_at_eseg;
146 wake_thunk16_Xgt_desc[1].size = 0x3ff;
147 wake_thunk16_Xgt_desc[1].address = 0;
148 wake_thunk16_Xgt_desc[2].size = 0;
149 wake_thunk16_Xgt_desc[2].address = 0;
151 /* Added this code to get current value of EIP. */
159 unsigned char *dest, *src;
160 src = (unsigned char *)dwEip;
161 dest = WAKE_RECOVER1M_CODE;
163 for (i = 0; i < 0x200; i++)
166 __asm__ __volatile__("ljmp $0x0010,%0" /* 08 error */
167 ::"i"((void *)(WAKE_RECOVER1M_CODE + 0x20)));
169 /* Added 0x20 "nop" to make sure the ljmp will not jump then halt. */
170 asm volatile ("nop");
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");
181 asm volatile ("nop");
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");
192 asm volatile ("nop");
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");
205 * Set new esp, maybe ebp should not equal to esp?, due to the
206 * variable in acpi_jump_wake?, anyway, this may be not a big
207 * problem. and I didn't clear the area (ef000+-0x200) to zero.
210 "movl %0, %%esp\n\t"::"a" (WAKE_THUNK16_STACK)
214 * Only "src" and "dest" use the new stack, and the esp maybe also
215 * used in resumevector.
217 #if PAYLOAD_IS_SEABIOS == 1
218 /* WAKE_MEM_INFO inited in get_set_top_available_mem in tables.c. */
220 (unsigned char *)((*(u32 *) WAKE_MEM_INFO) - 64 * 1024 - 0x100000);
224 * If recovered 0-e0000, then when resume, before WinXP turn on the
225 * desktop screen, there is gray background which last 1sec.
227 for (i = 0; i < 0xa0000; i++)
234 "movl $0xa0000, %%ecx\n\t"
240 src = (unsigned char *)((*(u32 *) WAKE_MEM_INFO) - 64 * 1024
241 - 0x100000 + 0xc0000);
245 for (i = 0; i < 0x20000; i++)
250 "movl $0xc0000, %%edi\n\t"
251 "movl $0x20000, %%ecx\n\t"
258 src = (unsigned char *)((*(u32 *) WAKE_MEM_INFO) - 64 * 1024
259 - 0x100000 + 0xe0000 + WAKE_SPECIAL_SIZE);
261 /* dest = 0xf0000; */
262 /* for (i = 0; i < 0x10000; i++) */
263 /* dest[i] = src[i]; */
269 "rep movsd\n\t"::"r" (src),
270 "r"(0xe0000 + WAKE_SPECIAL_SIZE),
271 "r"(0x10000 - WAKE_SPECIAL_SIZE)
274 src = (unsigned char *)((*(u32 *) WAKE_MEM_INFO) - 64 * 1024
275 - 0x100000 + 0xf0000);
276 /* dest = 0xf0000; */
277 /* for (i = 0; i < 0x10000; i++) */
278 /* dest[i] = src[i]; */
281 "movl $0xf0000, %%edi\n\t"
282 "movl $0x10000, %%ecx\n\t"
283 "shrl $2, %%ecx\n\t" "rep movsd\n\t"::"a" (src)
286 asm volatile ("wbinvd");
288 /* Set up the IDT for real mode. */
289 asm volatile ("lidt %0"::"m" (wake_thunk16_Xgt_desc[1]));
292 * Set up a GDT from which we can load segment descriptors for real
293 * mode. The GDT is not used in real mode; it is just needed here to
294 * prepare the descriptors.
296 asm volatile ("lgdt %0"::"m" (wake_thunk16_Xgt_desc[0]));
299 * Load the data segment registers, and thus the descriptors ready for
300 * real mode. The base address of each segment is 0x100, 16 times the
301 * selector value being loaded here. This is so that the segment
302 * registers don't have to be reloaded after switching to real mode:
303 * the values are consistent for real mode operation already.
305 __asm__ __volatile__(
306 "movl $0x0010,%%eax\n"
307 "\tmovl %%eax,%%ds\n"
308 "\tmovl %%eax,%%es\n"
309 "\tmovl %%eax,%%fs\n"
310 "\tmovl %%eax,%%gs\n"
311 "\tmovl %%eax,%%ss":::"eax"
315 * Jump to the 16-bit code that we copied earlier. It disables paging
316 * and the cache, switches to real mode, and jumps to the BIOS reset
320 __asm__ __volatile__(
321 "ljmp $0x0008,%0"::"i"
322 ((void *)(WAKE_THUNK16_ADDR - sizeof(real_mode_switch) - 100))
326 /* -*- linux-c -*- ------------------------------------------------------- *
328 * Copyright (C) 1991, 1992 Linus Torvalds
329 * Copyright 2007 rPath, Inc. - All Rights Reserved
331 * This file is part of the Linux kernel, and is made available under
332 * the terms of the GNU General Public License version 2.
334 * ----------------------------------------------------------------------- */
337 * arch/i386/boot/a20.c
339 * Enable A20 gate (return -1 on failure)
344 #define MAX_8042_LOOPS 100000
346 static int empty_8042(void)
349 int loops = MAX_8042_LOOPS;
356 /* Read and discard input data */
359 } else if (!(status & 2)) {
360 /* Buffers empty, finished! */
368 /* Returns nonzero if the A20 line is enabled. The memory address
369 used as a test is the int $0x80 vector, which should be safe. */
371 #define A20_TEST_ADDR (4*0x80)
372 #define A20_TEST_SHORT 32
373 #define A20_TEST_LONG 2097152 /* 2^21 */
375 static int a20_test(int loops)
383 saved = ctr = *((u32 *) A20_TEST_ADDR);
386 //wrfs32(++ctr, A20_TEST_ADDR);
388 *((u32 *) A20_TEST_ADDR) = ++ctr;
390 udelay(1); /* Serialize and make delay constant */
392 ok = *((u32 *) A20_TEST_ADDR + 0xffff0 + 0x10) ^ ctr;
397 *((u32 *) A20_TEST_ADDR) = saved;
401 /* Quick test to see if A20 is already enabled */
402 static int a20_test_short(void)
404 return a20_test(A20_TEST_SHORT);
407 /* Longer test that actually waits for A20 to come on line; this
408 is useful when dealing with the KBC or other slow external circuitry. */
409 static int a20_test_long(void)
411 return a20_test(A20_TEST_LONG);
414 static void enable_a20_kbc(void)
418 outb(0xd1, 0x64); /* Command write */
421 outb(0xdf, 0x60); /* A20 on */
425 static void enable_a20_fast(void)
429 port_a = inb(0x92); /* Configuration port A */
430 port_a |= 0x02; /* Enable A20 */
431 port_a &= ~0x01; /* Do not reset machine */
436 * Actual routine to enable A20; return 0 on ok, -1 on failure
439 #define A20_ENABLE_LOOPS 255 /* Number of times to try */
443 int loops = A20_ENABLE_LOOPS;
446 /* First, check to see if A20 is already enabled
447 (legacy free, etc.) */
448 if (a20_test_short())
451 /* Try enabling A20 through the keyboard controller */
454 // if (a20_test_short())
455 // return 0; /* BIOS worked, but with delayed reaction */
461 /* Finally, try enabling the "fast A20 gate" */