Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/x86/realmode/rm/reboot.S
26490 views
1
/* SPDX-License-Identifier: GPL-2.0 */
2
#include <linux/linkage.h>
3
#include <asm/desc_defs.h>
4
#include <asm/segment.h>
5
#include <asm/page_types.h>
6
#include <asm/processor-flags.h>
7
#include <asm/msr-index.h>
8
#include "realmode.h"
9
10
/*
11
* The following code and data reboots the machine by switching to real
12
* mode and jumping to the BIOS reset entry point, as if the CPU has
13
* really been reset. The previous version asked the keyboard
14
* controller to pulse the CPU reset line, which is more thorough, but
15
* doesn't work with at least one type of 486 motherboard. It is easy
16
* to stop this code working; hence the copious comments.
17
*
18
* This code is called with the restart type (0 = BIOS, 1 = APM) in
19
* the primary argument register (%eax for 32 bit, %edi for 64 bit).
20
*/
21
.section ".text32", "ax"
22
.code32
23
SYM_CODE_START(machine_real_restart_asm)
24
25
#ifdef CONFIG_X86_64
26
/* Switch to trampoline GDT as it is guaranteed < 4 GiB */
27
movl $__KERNEL_DS, %eax
28
movl %eax, %ds
29
lgdtl pa_tr_gdt
30
31
/* Disable paging to drop us out of long mode */
32
movl %cr0, %eax
33
andl $~X86_CR0_PG, %eax
34
movl %eax, %cr0
35
ljmpl $__KERNEL32_CS, $pa_machine_real_restart_paging_off
36
37
SYM_INNER_LABEL(machine_real_restart_paging_off, SYM_L_GLOBAL)
38
xorl %eax, %eax
39
xorl %edx, %edx
40
movl $MSR_EFER, %ecx
41
wrmsr
42
43
movl %edi, %eax
44
45
#endif /* CONFIG_X86_64 */
46
47
/* Set up the IDT for real mode. */
48
lidtl pa_machine_real_restart_idt
49
50
/*
51
* Set up a GDT from which we can load segment descriptors for real
52
* mode. The GDT is not used in real mode; it is just needed here to
53
* prepare the descriptors.
54
*/
55
lgdtl pa_machine_real_restart_gdt
56
57
/*
58
* Load the data segment registers with 16-bit compatible values
59
*/
60
movl $16, %ecx
61
movl %ecx, %ds
62
movl %ecx, %es
63
movl %ecx, %fs
64
movl %ecx, %gs
65
movl %ecx, %ss
66
ljmpw $8, $1f
67
SYM_CODE_END(machine_real_restart_asm)
68
69
/*
70
* This is 16-bit protected mode code to disable paging and the cache,
71
* switch to real mode and jump to the BIOS reset code.
72
*
73
* The instruction that switches to real mode by writing to CR0 must be
74
* followed immediately by a far jump instruction, which set CS to a
75
* valid value for real mode, and flushes the prefetch queue to avoid
76
* running instructions that have already been decoded in protected
77
* mode.
78
*
79
* Clears all the flags except ET, especially PG (paging), PE
80
* (protected-mode enable) and TS (task switch for coprocessor state
81
* save). Flushes the TLB after paging has been disabled. Sets CD and
82
* NW, to disable the cache on a 486, and invalidates the cache. This
83
* is more like the state of a 486 after reset. I don't know if
84
* something else should be done for other chips.
85
*
86
* More could be done here to set up the registers as if a CPU reset had
87
* occurred; hopefully real BIOSs don't assume much. This is not the
88
* actual BIOS entry point, anyway (that is at 0xfffffff0).
89
*
90
* Most of this work is probably excessive, but it is what is tested.
91
*/
92
.text
93
.code16
94
95
.balign 16
96
machine_real_restart_asm16:
97
1:
98
xorl %ecx, %ecx
99
movl %cr0, %edx
100
andl $0x00000011, %edx
101
orl $0x60000000, %edx
102
movl %edx, %cr0
103
movl %ecx, %cr3
104
movl %cr0, %edx
105
testl $0x60000000, %edx /* If no cache bits -> no wbinvd */
106
jz 2f
107
wbinvd
108
2:
109
andb $0x10, %dl
110
movl %edx, %cr0
111
LJMPW_RM(3f)
112
3:
113
andw %ax, %ax
114
jz bios
115
116
apm:
117
movw $0x1000, %ax
118
movw %ax, %ss
119
movw $0xf000, %sp
120
movw $0x5307, %ax
121
movw $0x0001, %bx
122
movw $0x0003, %cx
123
int $0x15
124
/* This should never return... */
125
126
bios:
127
ljmpw $0xf000, $0xfff0
128
129
.section ".rodata", "a"
130
131
.balign 16
132
SYM_DATA_START(machine_real_restart_idt)
133
.word 0xffff /* Length - real mode default value */
134
.long 0 /* Base - real mode default value */
135
SYM_DATA_END(machine_real_restart_idt)
136
137
.balign 16
138
SYM_DATA_START(machine_real_restart_gdt)
139
/* Self-pointer */
140
.word 0xffff /* Length - real mode default value */
141
.long pa_machine_real_restart_gdt
142
.word 0
143
144
/*
145
* 16-bit code segment pointing to real_mode_seg
146
* Selector value 8
147
*/
148
.word 0xffff /* Limit */
149
.long 0x9b000000 + pa_real_mode_base
150
.word 0
151
152
/*
153
* 16-bit data segment with the selector value 16 = 0x10 and
154
* base value 0x100; since this is consistent with real mode
155
* semantics we don't have to reload the segments once CR0.PE = 0.
156
*/
157
.quad GDT_ENTRY(DESC_DATA16, 0x100, 0xffff)
158
SYM_DATA_END(machine_real_restart_gdt)
159
160