Commit d5d96ed2d89ea228bdc27980b8ee4c94422ddef5
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
Merge branch 'x86-reboot-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86/reboot changes from Ingo Molnar: "Now that the revampted x86 real-mode trampoline code is upstream and seems to be working well, we can extend the 64-bit reboot code to be as capable as the 32-bit one." * 'x86-reboot-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86-64, reboot: Be more paranoid in 64-bit reboot=bios x86, reboot: Drop redundant write of reboot_mode x86-64, reboot: Allow reboot=bios and reboot-cpu override on x86-64
Showing 8 changed files Side-by-side Diff
arch/x86/include/asm/emergency-restart.h
arch/x86/include/asm/realmode.h
arch/x86/include/asm/reboot.h
... | ... | @@ -18,8 +18,8 @@ |
18 | 18 | |
19 | 19 | void native_machine_crash_shutdown(struct pt_regs *regs); |
20 | 20 | void native_machine_shutdown(void); |
21 | -void machine_real_restart(unsigned int type); | |
22 | -/* These must match dispatch_table in reboot_32.S */ | |
21 | +void __noreturn machine_real_restart(unsigned int type); | |
22 | +/* These must match dispatch in arch/x86/realmore/rm/reboot.S */ | |
23 | 23 | #define MRR_BIOS 0 |
24 | 24 | #define MRR_APM 1 |
25 | 25 |
arch/x86/kernel/reboot.c
... | ... | @@ -22,14 +22,12 @@ |
22 | 22 | #include <asm/virtext.h> |
23 | 23 | #include <asm/cpu.h> |
24 | 24 | #include <asm/nmi.h> |
25 | +#include <asm/smp.h> | |
25 | 26 | |
26 | -#ifdef CONFIG_X86_32 | |
27 | -# include <linux/ctype.h> | |
28 | -# include <linux/mc146818rtc.h> | |
29 | -# include <asm/realmode.h> | |
30 | -#else | |
31 | -# include <asm/x86_init.h> | |
32 | -#endif | |
27 | +#include <linux/ctype.h> | |
28 | +#include <linux/mc146818rtc.h> | |
29 | +#include <asm/realmode.h> | |
30 | +#include <asm/x86_init.h> | |
33 | 31 | |
34 | 32 | /* |
35 | 33 | * Power off function, if any |
... | ... | @@ -51,7 +49,7 @@ |
51 | 49 | */ |
52 | 50 | static int reboot_default = 1; |
53 | 51 | |
54 | -#if defined(CONFIG_X86_32) && defined(CONFIG_SMP) | |
52 | +#ifdef CONFIG_SMP | |
55 | 53 | static int reboot_cpu = -1; |
56 | 54 | #endif |
57 | 55 | |
... | ... | @@ -69,8 +67,8 @@ |
69 | 67 | * reboot=b[ios] | s[mp] | t[riple] | k[bd] | e[fi] [, [w]arm | [c]old] | p[ci] |
70 | 68 | * warm Don't set the cold reboot flag |
71 | 69 | * cold Set the cold reboot flag |
72 | - * bios Reboot by jumping through the BIOS (only for X86_32) | |
73 | - * smp Reboot by executing reset on BSP or other CPU (only for X86_32) | |
70 | + * bios Reboot by jumping through the BIOS | |
71 | + * smp Reboot by executing reset on BSP or other CPU | |
74 | 72 | * triple Force a triple fault (init) |
75 | 73 | * kbd Use the keyboard controller. cold reset (default) |
76 | 74 | * acpi Use the RESET_REG in the FADT |
... | ... | @@ -97,7 +95,6 @@ |
97 | 95 | reboot_mode = 0; |
98 | 96 | break; |
99 | 97 | |
100 | -#ifdef CONFIG_X86_32 | |
101 | 98 | #ifdef CONFIG_SMP |
102 | 99 | case 's': |
103 | 100 | if (isdigit(*(str+1))) { |
... | ... | @@ -114,7 +111,6 @@ |
114 | 111 | #endif /* CONFIG_SMP */ |
115 | 112 | |
116 | 113 | case 'b': |
117 | -#endif | |
118 | 114 | case 'a': |
119 | 115 | case 'k': |
120 | 116 | case 't': |
... | ... | @@ -140,7 +136,6 @@ |
140 | 136 | __setup("reboot=", reboot_setup); |
141 | 137 | |
142 | 138 | |
143 | -#ifdef CONFIG_X86_32 | |
144 | 139 | /* |
145 | 140 | * Reboot options and system auto-detection code provided by |
146 | 141 | * Dell Inc. so their systems "just work". :-) |
147 | 142 | |
... | ... | @@ -160,11 +155,8 @@ |
160 | 155 | return 0; |
161 | 156 | } |
162 | 157 | |
163 | -void machine_real_restart(unsigned int type) | |
158 | +void __noreturn machine_real_restart(unsigned int type) | |
164 | 159 | { |
165 | - void (*restart_lowmem)(unsigned int) = (void (*)(unsigned int)) | |
166 | - real_mode_header->machine_real_restart_asm; | |
167 | - | |
168 | 160 | local_irq_disable(); |
169 | 161 | |
170 | 162 | /* |
171 | 163 | |
172 | 164 | |
173 | 165 | |
174 | 166 | |
... | ... | @@ -184,25 +176,28 @@ |
184 | 176 | /* |
185 | 177 | * Switch back to the initial page table. |
186 | 178 | */ |
179 | +#ifdef CONFIG_X86_32 | |
187 | 180 | load_cr3(initial_page_table); |
181 | +#else | |
182 | + write_cr3(real_mode_header->trampoline_pgd); | |
183 | +#endif | |
188 | 184 | |
189 | - /* | |
190 | - * Write 0x1234 to absolute memory location 0x472. The BIOS reads | |
191 | - * this on booting to tell it to "Bypass memory test (also warm | |
192 | - * boot)". This seems like a fairly standard thing that gets set by | |
193 | - * REBOOT.COM programs, and the previous reset routine did this | |
194 | - * too. */ | |
195 | - *((unsigned short *)0x472) = reboot_mode; | |
196 | - | |
197 | 185 | /* Jump to the identity-mapped low memory code */ |
198 | - restart_lowmem(type); | |
186 | +#ifdef CONFIG_X86_32 | |
187 | + asm volatile("jmpl *%0" : : | |
188 | + "rm" (real_mode_header->machine_real_restart_asm), | |
189 | + "a" (type)); | |
190 | +#else | |
191 | + asm volatile("ljmpl *%0" : : | |
192 | + "m" (real_mode_header->machine_real_restart_asm), | |
193 | + "D" (type)); | |
194 | +#endif | |
195 | + unreachable(); | |
199 | 196 | } |
200 | 197 | #ifdef CONFIG_APM_MODULE |
201 | 198 | EXPORT_SYMBOL(machine_real_restart); |
202 | 199 | #endif |
203 | 200 | |
204 | -#endif /* CONFIG_X86_32 */ | |
205 | - | |
206 | 201 | /* |
207 | 202 | * Some Apple MacBook and MacBookPro's needs reboot=p to be able to reboot |
208 | 203 | */ |
209 | 204 | |
... | ... | @@ -227,11 +222,9 @@ |
227 | 222 | } |
228 | 223 | |
229 | 224 | /* |
230 | - * This is a single dmi_table handling all reboot quirks. Note that | |
231 | - * REBOOT_BIOS is only available for 32bit | |
225 | + * This is a single dmi_table handling all reboot quirks. | |
232 | 226 | */ |
233 | 227 | static struct dmi_system_id __initdata reboot_dmi_table[] = { |
234 | -#ifdef CONFIG_X86_32 | |
235 | 228 | { /* Handle problems with rebooting on Dell E520's */ |
236 | 229 | .callback = set_bios_reboot, |
237 | 230 | .ident = "Dell E520", |
... | ... | @@ -381,7 +374,6 @@ |
381 | 374 | DMI_MATCH(DMI_BOARD_NAME, "P4S800"), |
382 | 375 | }, |
383 | 376 | }, |
384 | -#endif /* CONFIG_X86_32 */ | |
385 | 377 | |
386 | 378 | { /* Handle reboot issue on Acer Aspire one */ |
387 | 379 | .callback = set_kbd_reboot, |
388 | 380 | |
... | ... | @@ -588,13 +580,11 @@ |
588 | 580 | reboot_type = BOOT_KBD; |
589 | 581 | break; |
590 | 582 | |
591 | -#ifdef CONFIG_X86_32 | |
592 | 583 | case BOOT_BIOS: |
593 | 584 | machine_real_restart(MRR_BIOS); |
594 | 585 | |
595 | 586 | reboot_type = BOOT_KBD; |
596 | 587 | break; |
597 | -#endif | |
598 | 588 | |
599 | 589 | case BOOT_ACPI: |
600 | 590 | acpi_reboot(); |
601 | 591 | |
... | ... | @@ -636,12 +626,10 @@ |
636 | 626 | /* The boot cpu is always logical cpu 0 */ |
637 | 627 | int reboot_cpu_id = 0; |
638 | 628 | |
639 | -#ifdef CONFIG_X86_32 | |
640 | 629 | /* See if there has been given a command line override */ |
641 | 630 | if ((reboot_cpu != -1) && (reboot_cpu < nr_cpu_ids) && |
642 | 631 | cpu_online(reboot_cpu)) |
643 | 632 | reboot_cpu_id = reboot_cpu; |
644 | -#endif | |
645 | 633 | |
646 | 634 | /* Make certain the cpu I'm about to reboot on is online */ |
647 | 635 | if (!cpu_online(reboot_cpu_id)) |
arch/x86/realmode/rm/Makefile
arch/x86/realmode/rm/header.S
... | ... | @@ -6,6 +6,7 @@ |
6 | 6 | |
7 | 7 | #include <linux/linkage.h> |
8 | 8 | #include <asm/page_types.h> |
9 | +#include <asm/segment.h> | |
9 | 10 | |
10 | 11 | #include "realmode.h" |
11 | 12 | |
12 | 13 | |
... | ... | @@ -28,8 +29,9 @@ |
28 | 29 | .long pa_wakeup_header |
29 | 30 | #endif |
30 | 31 | /* APM/BIOS reboot */ |
31 | -#ifdef CONFIG_X86_32 | |
32 | 32 | .long pa_machine_real_restart_asm |
33 | +#ifdef CONFIG_X86_64 | |
34 | + .long __KERNEL32_CS | |
33 | 35 | #endif |
34 | 36 | END(real_mode_header) |
35 | 37 |
arch/x86/realmode/rm/reboot.S
1 | +#include <linux/linkage.h> | |
2 | +#include <linux/init.h> | |
3 | +#include <asm/segment.h> | |
4 | +#include <asm/page_types.h> | |
5 | +#include <asm/processor-flags.h> | |
6 | +#include <asm/msr-index.h> | |
7 | +#include "realmode.h" | |
8 | + | |
9 | +/* | |
10 | + * The following code and data reboots the machine by switching to real | |
11 | + * mode and jumping to the BIOS reset entry point, as if the CPU has | |
12 | + * really been reset. The previous version asked the keyboard | |
13 | + * controller to pulse the CPU reset line, which is more thorough, but | |
14 | + * doesn't work with at least one type of 486 motherboard. It is easy | |
15 | + * to stop this code working; hence the copious comments. | |
16 | + * | |
17 | + * This code is called with the restart type (0 = BIOS, 1 = APM) in | |
18 | + * the primary argument register (%eax for 32 bit, %edi for 64 bit). | |
19 | + */ | |
20 | + .section ".text32", "ax" | |
21 | + .code32 | |
22 | +ENTRY(machine_real_restart_asm) | |
23 | + | |
24 | +#ifdef CONFIG_X86_64 | |
25 | + /* Switch to trampoline GDT as it is guaranteed < 4 GiB */ | |
26 | + movl $__KERNEL_DS, %eax | |
27 | + movl %eax, %ds | |
28 | + lgdtl pa_tr_gdt | |
29 | + | |
30 | + /* Disable paging to drop us out of long mode */ | |
31 | + movl %cr0, %eax | |
32 | + andl $~X86_CR0_PG, %eax | |
33 | + movl %eax, %cr0 | |
34 | + ljmpl $__KERNEL32_CS, $pa_machine_real_restart_paging_off | |
35 | + | |
36 | +GLOBAL(machine_real_restart_paging_off) | |
37 | + xorl %eax, %eax | |
38 | + xorl %edx, %edx | |
39 | + movl $MSR_EFER, %ecx | |
40 | + wrmsr | |
41 | + | |
42 | + movl %edi, %eax | |
43 | + | |
44 | +#endif /* CONFIG_X86_64 */ | |
45 | + | |
46 | + /* Set up the IDT for real mode. */ | |
47 | + lidtl pa_machine_real_restart_idt | |
48 | + | |
49 | + /* | |
50 | + * Set up a GDT from which we can load segment descriptors for real | |
51 | + * mode. The GDT is not used in real mode; it is just needed here to | |
52 | + * prepare the descriptors. | |
53 | + */ | |
54 | + lgdtl pa_machine_real_restart_gdt | |
55 | + | |
56 | + /* | |
57 | + * Load the data segment registers with 16-bit compatible values | |
58 | + */ | |
59 | + movl $16, %ecx | |
60 | + movl %ecx, %ds | |
61 | + movl %ecx, %es | |
62 | + movl %ecx, %fs | |
63 | + movl %ecx, %gs | |
64 | + movl %ecx, %ss | |
65 | + ljmpw $8, $1f | |
66 | + | |
67 | +/* | |
68 | + * This is 16-bit protected mode code to disable paging and the cache, | |
69 | + * switch to real mode and jump to the BIOS reset code. | |
70 | + * | |
71 | + * The instruction that switches to real mode by writing to CR0 must be | |
72 | + * followed immediately by a far jump instruction, which set CS to a | |
73 | + * valid value for real mode, and flushes the prefetch queue to avoid | |
74 | + * running instructions that have already been decoded in protected | |
75 | + * mode. | |
76 | + * | |
77 | + * Clears all the flags except ET, especially PG (paging), PE | |
78 | + * (protected-mode enable) and TS (task switch for coprocessor state | |
79 | + * save). Flushes the TLB after paging has been disabled. Sets CD and | |
80 | + * NW, to disable the cache on a 486, and invalidates the cache. This | |
81 | + * is more like the state of a 486 after reset. I don't know if | |
82 | + * something else should be done for other chips. | |
83 | + * | |
84 | + * More could be done here to set up the registers as if a CPU reset had | |
85 | + * occurred; hopefully real BIOSs don't assume much. This is not the | |
86 | + * actual BIOS entry point, anyway (that is at 0xfffffff0). | |
87 | + * | |
88 | + * Most of this work is probably excessive, but it is what is tested. | |
89 | + */ | |
90 | + .text | |
91 | + .code16 | |
92 | + | |
93 | + .balign 16 | |
94 | +machine_real_restart_asm16: | |
95 | +1: | |
96 | + xorl %ecx, %ecx | |
97 | + movl %cr0, %edx | |
98 | + andl $0x00000011, %edx | |
99 | + orl $0x60000000, %edx | |
100 | + movl %edx, %cr0 | |
101 | + movl %ecx, %cr3 | |
102 | + movl %cr0, %edx | |
103 | + testl $0x60000000, %edx /* If no cache bits -> no wbinvd */ | |
104 | + jz 2f | |
105 | + wbinvd | |
106 | +2: | |
107 | + andb $0x10, %dl | |
108 | + movl %edx, %cr0 | |
109 | + LJMPW_RM(3f) | |
110 | +3: | |
111 | + andw %ax, %ax | |
112 | + jz bios | |
113 | + | |
114 | +apm: | |
115 | + movw $0x1000, %ax | |
116 | + movw %ax, %ss | |
117 | + movw $0xf000, %sp | |
118 | + movw $0x5307, %ax | |
119 | + movw $0x0001, %bx | |
120 | + movw $0x0003, %cx | |
121 | + int $0x15 | |
122 | + /* This should never return... */ | |
123 | + | |
124 | +bios: | |
125 | + ljmpw $0xf000, $0xfff0 | |
126 | + | |
127 | + .section ".rodata", "a" | |
128 | + | |
129 | + .balign 16 | |
130 | +GLOBAL(machine_real_restart_idt) | |
131 | + .word 0xffff /* Length - real mode default value */ | |
132 | + .long 0 /* Base - real mode default value */ | |
133 | +END(machine_real_restart_idt) | |
134 | + | |
135 | + .balign 16 | |
136 | +GLOBAL(machine_real_restart_gdt) | |
137 | + /* Self-pointer */ | |
138 | + .word 0xffff /* Length - real mode default value */ | |
139 | + .long pa_machine_real_restart_gdt | |
140 | + .word 0 | |
141 | + | |
142 | + /* | |
143 | + * 16-bit code segment pointing to real_mode_seg | |
144 | + * Selector value 8 | |
145 | + */ | |
146 | + .word 0xffff /* Limit */ | |
147 | + .long 0x9b000000 + pa_real_mode_base | |
148 | + .word 0 | |
149 | + | |
150 | + /* | |
151 | + * 16-bit data segment with the selector value 16 = 0x10 and | |
152 | + * base value 0x100; since this is consistent with real mode | |
153 | + * semantics we don't have to reload the segments once CR0.PE = 0. | |
154 | + */ | |
155 | + .quad GDT_ENTRY(0x0093, 0x100, 0xffff) | |
156 | +END(machine_real_restart_gdt) |
arch/x86/realmode/rm/reboot_32.S
1 | -#include <linux/linkage.h> | |
2 | -#include <linux/init.h> | |
3 | -#include <asm/segment.h> | |
4 | -#include <asm/page_types.h> | |
5 | -#include "realmode.h" | |
6 | - | |
7 | -/* | |
8 | - * The following code and data reboots the machine by switching to real | |
9 | - * mode and jumping to the BIOS reset entry point, as if the CPU has | |
10 | - * really been reset. The previous version asked the keyboard | |
11 | - * controller to pulse the CPU reset line, which is more thorough, but | |
12 | - * doesn't work with at least one type of 486 motherboard. It is easy | |
13 | - * to stop this code working; hence the copious comments. | |
14 | - * | |
15 | - * This code is called with the restart type (0 = BIOS, 1 = APM) in %eax. | |
16 | - */ | |
17 | - .section ".text32", "ax" | |
18 | - .code32 | |
19 | - | |
20 | - .balign 16 | |
21 | -ENTRY(machine_real_restart_asm) | |
22 | - /* Set up the IDT for real mode. */ | |
23 | - lidtl pa_machine_real_restart_idt | |
24 | - | |
25 | - /* | |
26 | - * Set up a GDT from which we can load segment descriptors for real | |
27 | - * mode. The GDT is not used in real mode; it is just needed here to | |
28 | - * prepare the descriptors. | |
29 | - */ | |
30 | - lgdtl pa_machine_real_restart_gdt | |
31 | - | |
32 | - /* | |
33 | - * Load the data segment registers with 16-bit compatible values | |
34 | - */ | |
35 | - movl $16, %ecx | |
36 | - movl %ecx, %ds | |
37 | - movl %ecx, %es | |
38 | - movl %ecx, %fs | |
39 | - movl %ecx, %gs | |
40 | - movl %ecx, %ss | |
41 | - ljmpw $8, $1f | |
42 | - | |
43 | -/* | |
44 | - * This is 16-bit protected mode code to disable paging and the cache, | |
45 | - * switch to real mode and jump to the BIOS reset code. | |
46 | - * | |
47 | - * The instruction that switches to real mode by writing to CR0 must be | |
48 | - * followed immediately by a far jump instruction, which set CS to a | |
49 | - * valid value for real mode, and flushes the prefetch queue to avoid | |
50 | - * running instructions that have already been decoded in protected | |
51 | - * mode. | |
52 | - * | |
53 | - * Clears all the flags except ET, especially PG (paging), PE | |
54 | - * (protected-mode enable) and TS (task switch for coprocessor state | |
55 | - * save). Flushes the TLB after paging has been disabled. Sets CD and | |
56 | - * NW, to disable the cache on a 486, and invalidates the cache. This | |
57 | - * is more like the state of a 486 after reset. I don't know if | |
58 | - * something else should be done for other chips. | |
59 | - * | |
60 | - * More could be done here to set up the registers as if a CPU reset had | |
61 | - * occurred; hopefully real BIOSs don't assume much. This is not the | |
62 | - * actual BIOS entry point, anyway (that is at 0xfffffff0). | |
63 | - * | |
64 | - * Most of this work is probably excessive, but it is what is tested. | |
65 | - */ | |
66 | - .text | |
67 | - .code16 | |
68 | - | |
69 | - .balign 16 | |
70 | -machine_real_restart_asm16: | |
71 | -1: | |
72 | - xorl %ecx, %ecx | |
73 | - movl %cr0, %edx | |
74 | - andl $0x00000011, %edx | |
75 | - orl $0x60000000, %edx | |
76 | - movl %edx, %cr0 | |
77 | - movl %ecx, %cr3 | |
78 | - movl %cr0, %edx | |
79 | - testl $0x60000000, %edx /* If no cache bits -> no wbinvd */ | |
80 | - jz 2f | |
81 | - wbinvd | |
82 | -2: | |
83 | - andb $0x10, %dl | |
84 | - movl %edx, %cr0 | |
85 | - LJMPW_RM(3f) | |
86 | -3: | |
87 | - andw %ax, %ax | |
88 | - jz bios | |
89 | - | |
90 | -apm: | |
91 | - movw $0x1000, %ax | |
92 | - movw %ax, %ss | |
93 | - movw $0xf000, %sp | |
94 | - movw $0x5307, %ax | |
95 | - movw $0x0001, %bx | |
96 | - movw $0x0003, %cx | |
97 | - int $0x15 | |
98 | - /* This should never return... */ | |
99 | - | |
100 | -bios: | |
101 | - ljmpw $0xf000, $0xfff0 | |
102 | - | |
103 | - .section ".rodata", "a" | |
104 | - | |
105 | - .balign 16 | |
106 | -GLOBAL(machine_real_restart_idt) | |
107 | - .word 0xffff /* Length - real mode default value */ | |
108 | - .long 0 /* Base - real mode default value */ | |
109 | -END(machine_real_restart_idt) | |
110 | - | |
111 | - .balign 16 | |
112 | -GLOBAL(machine_real_restart_gdt) | |
113 | - /* Self-pointer */ | |
114 | - .word 0xffff /* Length - real mode default value */ | |
115 | - .long pa_machine_real_restart_gdt | |
116 | - .word 0 | |
117 | - | |
118 | - /* | |
119 | - * 16-bit code segment pointing to real_mode_seg | |
120 | - * Selector value 8 | |
121 | - */ | |
122 | - .word 0xffff /* Limit */ | |
123 | - .long 0x9b000000 + pa_real_mode_base | |
124 | - .word 0 | |
125 | - | |
126 | - /* | |
127 | - * 16-bit data segment with the selector value 16 = 0x10 and | |
128 | - * base value 0x100; since this is consistent with real mode | |
129 | - * semantics we don't have to reload the segments once CR0.PE = 0. | |
130 | - */ | |
131 | - .quad GDT_ENTRY(0x0093, 0x100, 0xffff) | |
132 | -END(machine_real_restart_gdt) |