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 - r_base), %ebx
25
26	/* Compute the equivalent real-mode segment */
27	movl	%ebx, %ecx
28	shrl	$4, %ecx
29
30	/* Patch post-real-mode segment jump */
31	movw	(dispatch_table - r_base)(%ebx,%eax,2),%ax
32	movw	%ax, (101f - r_base)(%ebx)
33	movw	%cx, (102f - r_base)(%ebx)
34
35	/* Set up the IDT for real mode. */
36	lidtl	(machine_real_restart_idt - r_base)(%ebx)
37
38	/*
39	 * Set up a GDT from which we can load segment descriptors for real
40	 * mode.  The GDT is not used in real mode; it is just needed here to
41	 * prepare the descriptors.
42	 */
43	lgdtl	(machine_real_restart_gdt - r_base)(%ebx)
44
45	/*
46	 * Load the data segment registers with 16-bit compatible values
47	 */
48	movl	$16, %ecx
49	movl	%ecx, %ds
50	movl	%ecx, %es
51	movl	%ecx, %fs
52	movl	%ecx, %gs
53	movl	%ecx, %ss
54	ljmpl	$8, $1f - r_base
55
56/*
57 * This is 16-bit protected mode code to disable paging and the cache,
58 * switch to real mode and jump to the BIOS reset code.
59 *
60 * The instruction that switches to real mode by writing to CR0 must be
61 * followed immediately by a far jump instruction, which set CS to a
62 * valid value for real mode, and flushes the prefetch queue to avoid
63 * running instructions that have already been decoded in protected
64 * mode.
65 *
66 * Clears all the flags except ET, especially PG (paging), PE
67 * (protected-mode enable) and TS (task switch for coprocessor state
68 * save).  Flushes the TLB after paging has been disabled.  Sets CD and
69 * NW, to disable the cache on a 486, and invalidates the cache.  This
70 * is more like the state of a 486 after reset.  I don't know if
71 * something else should be done for other chips.
72 *
73 * More could be done here to set up the registers as if a CPU reset had
74 * occurred; hopefully real BIOSs don't assume much.  This is not the
75 * actual BIOS entry point, anyway (that is at 0xfffffff0).
76 *
77 * Most of this work is probably excessive, but it is what is tested.
78 */
79	.code16
801:
81	xorl	%ecx, %ecx
82	movl	%cr0, %eax
83	andl	$0x00000011, %eax
84	orl	$0x60000000, %eax
85	movl	%eax, %cr0
86	movl	%ecx, %cr3
87	movl	%cr0, %edx
88	andl	$0x60000000, %edx	/* If no cache bits -> no wbinvd */
89	jz	2f
90	wbinvd
912:
92	andb	$0x10, %al
93	movl	%eax, %cr0
94	.byte	0xea			/* ljmpw */
95101:	.word	0			/* Offset */
96102:	.word	0			/* Segment */
97
98bios:
99	ljmpw	$0xf000, $0xfff0
100
101apm:
102	movw	$0x1000, %ax
103	movw	%ax, %ss
104	movw	$0xf000, %sp
105	movw	$0x5307, %ax
106	movw	$0x0001, %bx
107	movw	$0x0003, %cx
108	int	$0x15
109
110END(machine_real_restart_asm)
111
112	.balign 16
113	/* These must match <asm/reboot.h */
114dispatch_table:
115	.word	bios - r_base
116	.word	apm - r_base
117END(dispatch_table)
118
119	.balign 16
120machine_real_restart_idt:
121	.word	0xffff		/* Length - real mode default value */
122	.long	0		/* Base - real mode default value */
123END(machine_real_restart_idt)
124
125	.balign 16
126ENTRY(machine_real_restart_gdt)
127	.quad	0		/* Self-pointer, filled in by PM code */
128	.quad	0		/* 16-bit code segment, filled in by PM code */
129	/*
130	 * 16-bit data segment with the selector value 16 = 0x10 and
131	 * base value 0x100; since this is consistent with real mode
132	 * semantics we don't have to reload the segments once CR0.PE = 0.
133	 */
134	.quad	GDT_ENTRY(0x0093, 0x100, 0xffff)
135END(machine_real_restart_gdt)
136