aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/reboot.c
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@linux.intel.com>2011-02-14 21:36:03 -0500
committerH. Peter Anvin <hpa@linux.intel.com>2011-02-18 00:05:34 -0500
commit3d35ac346e981162eeba391e496faceed4753e7b (patch)
tree1f902bd0b8d62aa65b9d216483cbecc9a712a72b /arch/x86/kernel/reboot.c
parent014eea518af3d141e276664cf40ef3da899eba35 (diff)
x86, reboot: Move the real-mode reboot code to an assembly file
Move the real-mode reboot code out to an assembly file (reboot_32.S) which is allocated using the common lowmem trampoline allocator. Signed-off-by: H. Peter Anvin <hpa@linux.intel.com> LKML-Reference: <4D5DFBE4.7090104@intel.com> Cc: Stephen Rothwell <sfr@canb.auug.org.au> Cc: Rafael J. Wysocki <rjw@sisk.pl> Cc: Matthieu Castet <castet.matthieu@free.fr>
Diffstat (limited to 'arch/x86/kernel/reboot.c')
-rw-r--r--arch/x86/kernel/reboot.c120
1 files changed, 25 insertions, 95 deletions
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index fc7aae1e2bc7..10c6619c0543 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -295,68 +295,16 @@ static int __init reboot_init(void)
295} 295}
296core_initcall(reboot_init); 296core_initcall(reboot_init);
297 297
298/* The following code and data reboots the machine by switching to real 298extern const unsigned char machine_real_restart_asm[];
299 mode and jumping to the BIOS reset entry point, as if the CPU has 299extern const u64 machine_real_restart_gdt[3];
300 really been reset. The previous version asked the keyboard
301 controller to pulse the CPU reset line, which is more thorough, but
302 doesn't work with at least one type of 486 motherboard. It is easy
303 to stop this code working; hence the copious comments. */
304static const unsigned long long
305real_mode_gdt_entries [3] =
306{
307 0x0000000000000000ULL, /* Null descriptor */
308 0x00009b000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */
309 0x000093000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */
310};
311 300
312static const struct desc_ptr 301void machine_real_restart(unsigned int type)
313real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, (long)real_mode_gdt_entries },
314real_mode_idt = { 0x3ff, 0 };
315
316/* This is 16-bit protected mode code to disable paging and the cache,
317 switch to real mode and jump to the BIOS reset code.
318
319 The instruction that switches to real mode by writing to CR0 must be
320 followed immediately by a far jump instruction, which set CS to a
321 valid value for real mode, and flushes the prefetch queue to avoid
322 running instructions that have already been decoded in protected
323 mode.
324
325 Clears all the flags except ET, especially PG (paging), PE
326 (protected-mode enable) and TS (task switch for coprocessor state
327 save). Flushes the TLB after paging has been disabled. Sets CD and
328 NW, to disable the cache on a 486, and invalidates the cache. This
329 is more like the state of a 486 after reset. I don't know if
330 something else should be done for other chips.
331
332 More could be done here to set up the registers as if a CPU reset had
333 occurred; hopefully real BIOSs don't assume much. */
334static const unsigned char real_mode_switch [] =
335{
336 0x66, 0x0f, 0x20, 0xc0, /* movl %cr0,%eax */
337 0x66, 0x83, 0xe0, 0x11, /* andl $0x00000011,%eax */
338 0x66, 0x0d, 0x00, 0x00, 0x00, 0x60, /* orl $0x60000000,%eax */
339 0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */
340 0x66, 0x0f, 0x22, 0xd8, /* movl %eax,%cr3 */
341 0x66, 0x0f, 0x20, 0xc3, /* movl %cr0,%ebx */
342 0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60, /* andl $0x60000000,%ebx */
343 0x74, 0x02, /* jz f */
344 0x0f, 0x09, /* wbinvd */
345 0x24, 0x10, /* f: andb $0x10,al */
346 0x66, 0x0f, 0x22, 0xc0 /* movl %eax,%cr0 */
347};
348static const unsigned char jump_to_bios [] =
349{ 302{
350 0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */ 303 void *restart_va;
351}; 304 unsigned long restart_pa;
305 void (*restart_lowmem)(unsigned int);
306 u64 *lowmem_gdt;
352 307
353/*
354 * Switch to real mode and then execute the code
355 * specified by the code and length parameters.
356 * We assume that length will aways be less that 100!
357 */
358void machine_real_restart(const unsigned char *code, int length)
359{
360 local_irq_disable(); 308 local_irq_disable();
361 309
362 /* Write zero to CMOS register number 0x0f, which the BIOS POST 310 /* Write zero to CMOS register number 0x0f, which the BIOS POST
@@ -384,41 +332,23 @@ void machine_real_restart(const unsigned char *code, int length)
384 too. */ 332 too. */
385 *((unsigned short *)0x472) = reboot_mode; 333 *((unsigned short *)0x472) = reboot_mode;
386 334
387 /* For the switch to real mode, copy some code to low memory. It has 335 /* Patch the GDT in the low memory trampoline */
388 to be in the first 64k because it is running in 16-bit mode, and it 336 lowmem_gdt = TRAMPOLINE_SYM(machine_real_restart_gdt);
389 has to have the same physical and virtual address, because it turns 337
390 off paging. Copy it near the end of the first page, out of the way 338 restart_va = TRAMPOLINE_SYM(machine_real_restart_asm);
391 of BIOS variables. */ 339 restart_pa = virt_to_phys(restart_va);
392 memcpy((void *)(0x1000 - sizeof(real_mode_switch) - 100), 340 restart_lowmem = (void (*)(unsigned int))restart_pa;
393 real_mode_switch, sizeof (real_mode_switch)); 341
394 memcpy((void *)(0x1000 - 100), code, length); 342 /* GDT[0]: GDT self-pointer */
395 343 lowmem_gdt[0] =
396 /* Set up the IDT for real mode. */ 344 (u64)(sizeof(machine_real_restart_gdt) - 1) +
397 load_idt(&real_mode_idt); 345 ((u64)virt_to_phys(lowmem_gdt) << 16);
398 346 /* GDT[1]: 64K real mode code segment */
399 /* Set up a GDT from which we can load segment descriptors for real 347 lowmem_gdt[1] =
400 mode. The GDT is not used in real mode; it is just needed here to 348 GDT_ENTRY(0x009b, restart_pa, 0xffff);
401 prepare the descriptors. */ 349
402 load_gdt(&real_mode_gdt); 350 /* Jump to the identity-mapped low memory code */
403 351 restart_lowmem(type);
404 /* Load the data segment registers, and thus the descriptors ready for
405 real mode. The base address of each segment is 0x100, 16 times the
406 selector value being loaded here. This is so that the segment
407 registers don't have to be reloaded after switching to real mode:
408 the values are consistent for real mode operation already. */
409 __asm__ __volatile__ ("movl $0x0010,%%eax\n"
410 "\tmovl %%eax,%%ds\n"
411 "\tmovl %%eax,%%es\n"
412 "\tmovl %%eax,%%fs\n"
413 "\tmovl %%eax,%%gs\n"
414 "\tmovl %%eax,%%ss" : : : "eax");
415
416 /* Jump to the 16-bit code that we copied earlier. It disables paging
417 and the cache, switches to real mode, and jumps to the BIOS reset
418 entry point. */
419 __asm__ __volatile__ ("ljmp $0x0008,%0"
420 :
421 : "i" ((void *)(0x1000 - sizeof (real_mode_switch) - 100)));
422} 352}
423#ifdef CONFIG_APM_MODULE 353#ifdef CONFIG_APM_MODULE
424EXPORT_SYMBOL(machine_real_restart); 354EXPORT_SYMBOL(machine_real_restart);
@@ -573,7 +503,7 @@ static void native_machine_emergency_restart(void)
573 503
574#ifdef CONFIG_X86_32 504#ifdef CONFIG_X86_32
575 case BOOT_BIOS: 505 case BOOT_BIOS:
576 machine_real_restart(jump_to_bios, sizeof(jump_to_bios)); 506 machine_real_restart(MRR_BIOS);
577 507
578 reboot_type = BOOT_KBD; 508 reboot_type = BOOT_KBD;
579 break; 509 break;