|
@@ -295,68 +295,16 @@ static int __init reboot_init(void)
|
|
|
}
|
|
|
core_initcall(reboot_init);
|
|
|
|
|
|
-/* The following code and data reboots the machine by switching to real
|
|
|
- mode and jumping to the BIOS reset entry point, as if the CPU has
|
|
|
- really been reset. The previous version asked the keyboard
|
|
|
- controller to pulse the CPU reset line, which is more thorough, but
|
|
|
- doesn't work with at least one type of 486 motherboard. It is easy
|
|
|
- to stop this code working; hence the copious comments. */
|
|
|
-static const unsigned long long
|
|
|
-real_mode_gdt_entries [3] =
|
|
|
-{
|
|
|
- 0x0000000000000000ULL, /* Null descriptor */
|
|
|
- 0x00009b000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */
|
|
|
- 0x000093000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */
|
|
|
-};
|
|
|
+extern const unsigned char machine_real_restart_asm[];
|
|
|
+extern const u64 machine_real_restart_gdt[3];
|
|
|
|
|
|
-static const struct desc_ptr
|
|
|
-real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, (long)real_mode_gdt_entries },
|
|
|
-real_mode_idt = { 0x3ff, 0 };
|
|
|
-
|
|
|
-/* This is 16-bit protected mode code to disable paging and the cache,
|
|
|
- switch to real mode and jump to the BIOS reset code.
|
|
|
-
|
|
|
- The instruction that switches to real mode by writing to CR0 must be
|
|
|
- followed immediately by a far jump instruction, which set CS to a
|
|
|
- valid value for real mode, and flushes the prefetch queue to avoid
|
|
|
- running instructions that have already been decoded in protected
|
|
|
- mode.
|
|
|
-
|
|
|
- Clears all the flags except ET, especially PG (paging), PE
|
|
|
- (protected-mode enable) and TS (task switch for coprocessor state
|
|
|
- save). Flushes the TLB after paging has been disabled. Sets CD and
|
|
|
- NW, to disable the cache on a 486, and invalidates the cache. This
|
|
|
- is more like the state of a 486 after reset. I don't know if
|
|
|
- something else should be done for other chips.
|
|
|
-
|
|
|
- More could be done here to set up the registers as if a CPU reset had
|
|
|
- occurred; hopefully real BIOSs don't assume much. */
|
|
|
-static const unsigned char real_mode_switch [] =
|
|
|
-{
|
|
|
- 0x66, 0x0f, 0x20, 0xc0, /* movl %cr0,%eax */
|
|
|
- 0x66, 0x83, 0xe0, 0x11, /* andl $0x00000011,%eax */
|
|
|
- 0x66, 0x0d, 0x00, 0x00, 0x00, 0x60, /* orl $0x60000000,%eax */
|
|
|
- 0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */
|
|
|
- 0x66, 0x0f, 0x22, 0xd8, /* movl %eax,%cr3 */
|
|
|
- 0x66, 0x0f, 0x20, 0xc3, /* movl %cr0,%ebx */
|
|
|
- 0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60, /* andl $0x60000000,%ebx */
|
|
|
- 0x74, 0x02, /* jz f */
|
|
|
- 0x0f, 0x09, /* wbinvd */
|
|
|
- 0x24, 0x10, /* f: andb $0x10,al */
|
|
|
- 0x66, 0x0f, 0x22, 0xc0 /* movl %eax,%cr0 */
|
|
|
-};
|
|
|
-static const unsigned char jump_to_bios [] =
|
|
|
+void machine_real_restart(unsigned int type)
|
|
|
{
|
|
|
- 0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */
|
|
|
-};
|
|
|
+ void *restart_va;
|
|
|
+ unsigned long restart_pa;
|
|
|
+ void (*restart_lowmem)(unsigned int);
|
|
|
+ u64 *lowmem_gdt;
|
|
|
|
|
|
-/*
|
|
|
- * Switch to real mode and then execute the code
|
|
|
- * specified by the code and length parameters.
|
|
|
- * We assume that length will aways be less that 100!
|
|
|
- */
|
|
|
-void machine_real_restart(const unsigned char *code, int length)
|
|
|
-{
|
|
|
local_irq_disable();
|
|
|
|
|
|
/* 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)
|
|
|
too. */
|
|
|
*((unsigned short *)0x472) = reboot_mode;
|
|
|
|
|
|
- /* For the switch to real mode, copy some code to low memory. It has
|
|
|
- to be in the first 64k because it is running in 16-bit mode, and it
|
|
|
- has to have the same physical and virtual address, because it turns
|
|
|
- off paging. Copy it near the end of the first page, out of the way
|
|
|
- of BIOS variables. */
|
|
|
- memcpy((void *)(0x1000 - sizeof(real_mode_switch) - 100),
|
|
|
- real_mode_switch, sizeof (real_mode_switch));
|
|
|
- memcpy((void *)(0x1000 - 100), code, length);
|
|
|
-
|
|
|
- /* Set up the IDT for real mode. */
|
|
|
- load_idt(&real_mode_idt);
|
|
|
-
|
|
|
- /* Set up a GDT from which we can load segment descriptors for real
|
|
|
- mode. The GDT is not used in real mode; it is just needed here to
|
|
|
- prepare the descriptors. */
|
|
|
- load_gdt(&real_mode_gdt);
|
|
|
-
|
|
|
- /* Load the data segment registers, and thus the descriptors ready for
|
|
|
- real mode. The base address of each segment is 0x100, 16 times the
|
|
|
- selector value being loaded here. This is so that the segment
|
|
|
- registers don't have to be reloaded after switching to real mode:
|
|
|
- the values are consistent for real mode operation already. */
|
|
|
- __asm__ __volatile__ ("movl $0x0010,%%eax\n"
|
|
|
- "\tmovl %%eax,%%ds\n"
|
|
|
- "\tmovl %%eax,%%es\n"
|
|
|
- "\tmovl %%eax,%%fs\n"
|
|
|
- "\tmovl %%eax,%%gs\n"
|
|
|
- "\tmovl %%eax,%%ss" : : : "eax");
|
|
|
-
|
|
|
- /* Jump to the 16-bit code that we copied earlier. It disables paging
|
|
|
- and the cache, switches to real mode, and jumps to the BIOS reset
|
|
|
- entry point. */
|
|
|
- __asm__ __volatile__ ("ljmp $0x0008,%0"
|
|
|
- :
|
|
|
- : "i" ((void *)(0x1000 - sizeof (real_mode_switch) - 100)));
|
|
|
+ /* Patch the GDT in the low memory trampoline */
|
|
|
+ lowmem_gdt = TRAMPOLINE_SYM(machine_real_restart_gdt);
|
|
|
+
|
|
|
+ restart_va = TRAMPOLINE_SYM(machine_real_restart_asm);
|
|
|
+ restart_pa = virt_to_phys(restart_va);
|
|
|
+ restart_lowmem = (void (*)(unsigned int))restart_pa;
|
|
|
+
|
|
|
+ /* GDT[0]: GDT self-pointer */
|
|
|
+ lowmem_gdt[0] =
|
|
|
+ (u64)(sizeof(machine_real_restart_gdt) - 1) +
|
|
|
+ ((u64)virt_to_phys(lowmem_gdt) << 16);
|
|
|
+ /* GDT[1]: 64K real mode code segment */
|
|
|
+ lowmem_gdt[1] =
|
|
|
+ GDT_ENTRY(0x009b, restart_pa, 0xffff);
|
|
|
+
|
|
|
+ /* Jump to the identity-mapped low memory code */
|
|
|
+ restart_lowmem(type);
|
|
|
}
|
|
|
#ifdef CONFIG_APM_MODULE
|
|
|
EXPORT_SYMBOL(machine_real_restart);
|
|
@@ -573,7 +503,7 @@ static void native_machine_emergency_restart(void)
|
|
|
|
|
|
#ifdef CONFIG_X86_32
|
|
|
case BOOT_BIOS:
|
|
|
- machine_real_restart(jump_to_bios, sizeof(jump_to_bios));
|
|
|
+ machine_real_restart(MRR_BIOS);
|
|
|
|
|
|
reboot_type = BOOT_KBD;
|
|
|
break;
|