aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/reboot_32.S
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_32.S
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_32.S')
-rw-r--r--arch/x86/kernel/reboot_32.S131
1 files changed, 131 insertions, 0 deletions
diff --git a/arch/x86/kernel/reboot_32.S b/arch/x86/kernel/reboot_32.S
new file mode 100644
index 00000000000..f242356a096
--- /dev/null
+++ b/arch/x86/kernel/reboot_32.S
@@ -0,0 +1,131 @@
1#include <linux/linkage.h>
2#include <linux/init.h>
3#include <asm/segment.h>
4#include <asm/page_types.h>
5
6/*
7 * The following code and data reboots the machine by switching to real
8 * mode and jumping to the BIOS reset entry point, as if the CPU has
9 * really been reset. The previous version asked the keyboard
10 * controller to pulse the CPU reset line, which is more thorough, but
11 * doesn't work with at least one type of 486 motherboard. It is easy
12 * to stop this code working; hence the copious comments.
13 *
14 * This code is called with the restart type (0 = BIOS, 1 = APM) in %eax.
15 */
16 .section ".x86_trampoline","a"
17 .balign 16
18 .code32
19ENTRY(machine_real_restart_asm)
20r_base = .
21 /* Get our own relocated address */
22 call 1f
231: popl %ebx
24 subl $1b, %ebx
25
26 /* Patch post-real-mode segment jump */
27 movw dispatch_table(%ebx,%ecx,2),%cx
28 movw %cx, 101f(%ebx)
29 movw %ax, 102f(%ebx)
30
31 /* Set up the IDT for real mode. */
32 lidtl machine_real_restart_idt(%ebx)
33
34 /*
35 * Set up a GDT from which we can load segment descriptors for real
36 * mode. The GDT is not used in real mode; it is just needed here to
37 * prepare the descriptors.
38 */
39 lgdtl machine_real_restart_gdt(%ebx)
40
41 /*
42 * Load the data segment registers with 16-bit compatible values
43 */
44 movl $16, %ecx
45 movl %ecx, %ds
46 movl %ecx, %es
47 movl %ecx, %fs
48 movl %ecx, %gs
49 movl %ecx, %ss
50 ljmpl $8, $1f - r_base
51
52/*
53 * This is 16-bit protected mode code to disable paging and the cache,
54 * switch to real mode and jump to the BIOS reset code.
55 *
56 * The instruction that switches to real mode by writing to CR0 must be
57 * followed immediately by a far jump instruction, which set CS to a
58 * valid value for real mode, and flushes the prefetch queue to avoid
59 * running instructions that have already been decoded in protected
60 * mode.
61 *
62 * Clears all the flags except ET, especially PG (paging), PE
63 * (protected-mode enable) and TS (task switch for coprocessor state
64 * save). Flushes the TLB after paging has been disabled. Sets CD and
65 * NW, to disable the cache on a 486, and invalidates the cache. This
66 * is more like the state of a 486 after reset. I don't know if
67 * something else should be done for other chips.
68 *
69 * More could be done here to set up the registers as if a CPU reset had
70 * occurred; hopefully real BIOSs don't assume much. This is not the
71 * actual BIOS entry point, anyway (that is at 0xfffffff0).
72 *
73 * Most of this work is probably excessive, but it is what is tested.
74 */
75 .code16
761:
77 xorl %ecx, %ecx
78 movl %cr0, %eax
79 andl $0x00000011, %eax
80 orl $0x60000000, %eax
81 movl %eax, %cr0
82 movl %ecx, %cr3
83 movl %cr0, %edx
84 andl $0x60000000, %edx /* If no cache bits -> no wbinvd */
85 jz 2f
86 wbinvd
872:
88 andb $0x10, %al
89 movl %eax, %cr0
90 .byte 0xea /* ljmpw */
91101: .word 0 /* Offset */
92102: .word 0 /* Segment */
93
94bios:
95 ljmpw $0xf000, $0xfff0
96
97apm:
98 movw $0x1000, %ax
99 movw %ax, %ss
100 movw $0xf000, %sp
101 movw $0x5307, %ax
102 movw $0x0001, %bx
103 movw $0x0003, %cx
104 int $0x15
105
106END(machine_real_restart_asm)
107
108 .balign 16
109 /* These must match <asm/reboot.h */
110dispatch_table:
111 .word bios - r_base
112 .word apm - r_base
113END(dispatch_table)
114
115 .balign 16
116machine_real_restart_idt:
117 .word 0xffff /* Length - real mode default value */
118 .long 0 /* Base - real mode default value */
119END(machine_real_restart_idt)
120
121 .balign 16
122ENTRY(machine_real_restart_gdt)
123 .quad 0 /* Self-pointer, filled in by PM code */
124 .quad 0 /* 16-bit code segment, filled in by PM code */
125 /*
126 * 16-bit data segment with the selector value 16 = 0x10 and
127 * base value 0x100; since this is consistent with real mode
128 * semantics we don't have to reload the segments once CR0.PE = 0.
129 */
130 .quad GDT_ENTRY(0x0093, 0x100, 0xffff)
131END(machine_real_restart_gdt)