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 | |