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>tirimbino
parent
014eea518a
commit
3d35ac346e
@ -0,0 +1,131 @@ |
||||
#include <linux/linkage.h> |
||||
#include <linux/init.h> |
||||
#include <asm/segment.h> |
||||
#include <asm/page_types.h> |
||||
|
||||
/* |
||||
* 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.
|
||||
* |
||||
* This code is called with the restart type (0 = BIOS, 1 = APM) in %eax. |
||||
*/ |
||||
.section ".x86_trampoline","a" |
||||
.balign 16
|
||||
.code32 |
||||
ENTRY(machine_real_restart_asm) |
||||
r_base = . |
||||
/* Get our own relocated address */ |
||||
call 1f |
||||
1: popl %ebx |
||||
subl $1b, %ebx |
||||
|
||||
/* Patch post-real-mode segment jump */ |
||||
movw dispatch_table(%ebx,%ecx,2),%cx |
||||
movw %cx, 101f(%ebx) |
||||
movw %ax, 102f(%ebx) |
||||
|
||||
/* Set up the IDT for real mode. */ |
||||
lidtl machine_real_restart_idt(%ebx) |
||||
|
||||
/* |
||||
* 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. |
||||
*/ |
||||
lgdtl machine_real_restart_gdt(%ebx) |
||||
|
||||
/* |
||||
* Load the data segment registers with 16-bit compatible values |
||||
*/ |
||||
movl $16, %ecx |
||||
movl %ecx, %ds |
||||
movl %ecx, %es |
||||
movl %ecx, %fs |
||||
movl %ecx, %gs |
||||
movl %ecx, %ss |
||||
ljmpl $8, $1f - r_base |
||||
|
||||
/* |
||||
* 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. This is not the
|
||||
* actual BIOS entry point, anyway (that is at 0xfffffff0). |
||||
* |
||||
* Most of this work is probably excessive, but it is what is tested. |
||||
*/ |
||||
.code16 |
||||
1: |
||||
xorl %ecx, %ecx |
||||
movl %cr0, %eax |
||||
andl $0x00000011, %eax |
||||
orl $0x60000000, %eax |
||||
movl %eax, %cr0 |
||||
movl %ecx, %cr3 |
||||
movl %cr0, %edx |
||||
andl $0x60000000, %edx /* If no cache bits -> no wbinvd */ |
||||
jz 2f |
||||
wbinvd |
||||
2: |
||||
andb $0x10, %al |
||||
movl %eax, %cr0 |
||||
.byte 0xea /* ljmpw */ |
||||
101: .word 0 /* Offset */ |
||||
102: .word 0 /* Segment */ |
||||
|
||||
bios: |
||||
ljmpw $0xf000, $0xfff0 |
||||
|
||||
apm: |
||||
movw $0x1000, %ax |
||||
movw %ax, %ss |
||||
movw $0xf000, %sp |
||||
movw $0x5307, %ax |
||||
movw $0x0001, %bx |
||||
movw $0x0003, %cx |
||||
int $0x15 |
||||
|
||||
END(machine_real_restart_asm) |
||||
|
||||
.balign 16
|
||||
/* These must match <asm/reboot.h */ |
||||
dispatch_table: |
||||
.word bios - r_base |
||||
.word apm - r_base |
||||
END(dispatch_table) |
||||
|
||||
.balign 16
|
||||
machine_real_restart_idt: |
||||
.word 0xffff /* Length - real mode default value */ |
||||
.long 0 /* Base - real mode default value */ |
||||
END(machine_real_restart_idt) |
||||
|
||||
.balign 16
|
||||
ENTRY(machine_real_restart_gdt) |
||||
.quad 0 /* Self-pointer, filled in by PM code */ |
||||
.quad 0 /* 16-bit code segment, filled in by PM code */ |
||||
/* |
||||
* 16-bit data segment with the selector value 16 = 0x10 and |
||||
* base value 0x100; since this is consistent with real mode
|
||||
* semantics we don't have to reload the segments once CR0.PE = 0. |
||||
*/ |
||||
.quad GDT_ENTRY(0x0093, 0x100, 0xffff) |
||||
END(machine_real_restart_gdt) |
Loading…
Reference in new issue