diff options
-rw-r--r-- | arch/x86/include/asm/reboot.h | 5 | ||||
-rw-r--r-- | arch/x86/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/kernel/apm_32.c | 12 | ||||
-rw-r--r-- | arch/x86/kernel/reboot.c | 120 | ||||
-rw-r--r-- | arch/x86/kernel/reboot_32.S | 131 |
5 files changed, 162 insertions, 107 deletions
diff --git a/arch/x86/include/asm/reboot.h b/arch/x86/include/asm/reboot.h index 562d4fd31ba8..3250e3d605d9 100644 --- a/arch/x86/include/asm/reboot.h +++ b/arch/x86/include/asm/reboot.h | |||
@@ -18,7 +18,10 @@ extern struct machine_ops machine_ops; | |||
18 | 18 | ||
19 | void native_machine_crash_shutdown(struct pt_regs *regs); | 19 | void native_machine_crash_shutdown(struct pt_regs *regs); |
20 | void native_machine_shutdown(void); | 20 | void native_machine_shutdown(void); |
21 | void machine_real_restart(const unsigned char *code, int length); | 21 | void machine_real_restart(unsigned int type); |
22 | /* These must match dispatch_table in reboot_32.S */ | ||
23 | #define MRR_BIOS 0 | ||
24 | #define MRR_APM 1 | ||
22 | 25 | ||
23 | typedef void (*nmi_shootdown_cb)(int, struct die_args*); | 26 | typedef void (*nmi_shootdown_cb)(int, struct die_args*); |
24 | void nmi_shootdown_cpus(nmi_shootdown_cb callback); | 27 | void nmi_shootdown_cpus(nmi_shootdown_cb callback); |
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 2e8ce0deae4a..778c5b93676d 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile | |||
@@ -59,6 +59,7 @@ obj-$(CONFIG_STACKTRACE) += stacktrace.o | |||
59 | obj-y += cpu/ | 59 | obj-y += cpu/ |
60 | obj-y += acpi/ | 60 | obj-y += acpi/ |
61 | obj-y += reboot.o | 61 | obj-y += reboot.o |
62 | obj-$(CONFIG_X86_32) += reboot_32.o | ||
62 | obj-$(CONFIG_MCA) += mca_32.o | 63 | obj-$(CONFIG_MCA) += mca_32.o |
63 | obj-$(CONFIG_X86_MSR) += msr.o | 64 | obj-$(CONFIG_X86_MSR) += msr.o |
64 | obj-$(CONFIG_X86_CPUID) += cpuid.o | 65 | obj-$(CONFIG_X86_CPUID) += cpuid.o |
diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c index 0e4f24c2a746..b929108eb58f 100644 --- a/arch/x86/kernel/apm_32.c +++ b/arch/x86/kernel/apm_32.c | |||
@@ -975,20 +975,10 @@ recalc: | |||
975 | 975 | ||
976 | static void apm_power_off(void) | 976 | static void apm_power_off(void) |
977 | { | 977 | { |
978 | unsigned char po_bios_call[] = { | ||
979 | 0xb8, 0x00, 0x10, /* movw $0x1000,ax */ | ||
980 | 0x8e, 0xd0, /* movw ax,ss */ | ||
981 | 0xbc, 0x00, 0xf0, /* movw $0xf000,sp */ | ||
982 | 0xb8, 0x07, 0x53, /* movw $0x5307,ax */ | ||
983 | 0xbb, 0x01, 0x00, /* movw $0x0001,bx */ | ||
984 | 0xb9, 0x03, 0x00, /* movw $0x0003,cx */ | ||
985 | 0xcd, 0x15 /* int $0x15 */ | ||
986 | }; | ||
987 | |||
988 | /* Some bioses don't like being called from CPU != 0 */ | 978 | /* Some bioses don't like being called from CPU != 0 */ |
989 | if (apm_info.realmode_power_off) { | 979 | if (apm_info.realmode_power_off) { |
990 | set_cpus_allowed_ptr(current, cpumask_of(0)); | 980 | set_cpus_allowed_ptr(current, cpumask_of(0)); |
991 | machine_real_restart(po_bios_call, sizeof(po_bios_call)); | 981 | machine_real_restart(MRR_APM); |
992 | } else { | 982 | } else { |
993 | (void)set_system_power_state(APM_STATE_OFF); | 983 | (void)set_system_power_state(APM_STATE_OFF); |
994 | } | 984 | } |
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index fc7aae1e2bc7..10c6619c0543 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c | |||
@@ -295,68 +295,16 @@ static int __init reboot_init(void) | |||
295 | } | 295 | } |
296 | core_initcall(reboot_init); | 296 | core_initcall(reboot_init); |
297 | 297 | ||
298 | /* The following code and data reboots the machine by switching to real | 298 | extern const unsigned char machine_real_restart_asm[]; |
299 | mode and jumping to the BIOS reset entry point, as if the CPU has | 299 | extern const u64 machine_real_restart_gdt[3]; |
300 | really been reset. The previous version asked the keyboard | ||
301 | controller to pulse the CPU reset line, which is more thorough, but | ||
302 | doesn't work with at least one type of 486 motherboard. It is easy | ||
303 | to stop this code working; hence the copious comments. */ | ||
304 | static const unsigned long long | ||
305 | real_mode_gdt_entries [3] = | ||
306 | { | ||
307 | 0x0000000000000000ULL, /* Null descriptor */ | ||
308 | 0x00009b000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */ | ||
309 | 0x000093000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */ | ||
310 | }; | ||
311 | 300 | ||
312 | static const struct desc_ptr | 301 | void machine_real_restart(unsigned int type) |
313 | real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, (long)real_mode_gdt_entries }, | ||
314 | real_mode_idt = { 0x3ff, 0 }; | ||
315 | |||
316 | /* This is 16-bit protected mode code to disable paging and the cache, | ||
317 | switch to real mode and jump to the BIOS reset code. | ||
318 | |||
319 | The instruction that switches to real mode by writing to CR0 must be | ||
320 | followed immediately by a far jump instruction, which set CS to a | ||
321 | valid value for real mode, and flushes the prefetch queue to avoid | ||
322 | running instructions that have already been decoded in protected | ||
323 | mode. | ||
324 | |||
325 | Clears all the flags except ET, especially PG (paging), PE | ||
326 | (protected-mode enable) and TS (task switch for coprocessor state | ||
327 | save). Flushes the TLB after paging has been disabled. Sets CD and | ||
328 | NW, to disable the cache on a 486, and invalidates the cache. This | ||
329 | is more like the state of a 486 after reset. I don't know if | ||
330 | something else should be done for other chips. | ||
331 | |||
332 | More could be done here to set up the registers as if a CPU reset had | ||
333 | occurred; hopefully real BIOSs don't assume much. */ | ||
334 | static const unsigned char real_mode_switch [] = | ||
335 | { | ||
336 | 0x66, 0x0f, 0x20, 0xc0, /* movl %cr0,%eax */ | ||
337 | 0x66, 0x83, 0xe0, 0x11, /* andl $0x00000011,%eax */ | ||
338 | 0x66, 0x0d, 0x00, 0x00, 0x00, 0x60, /* orl $0x60000000,%eax */ | ||
339 | 0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */ | ||
340 | 0x66, 0x0f, 0x22, 0xd8, /* movl %eax,%cr3 */ | ||
341 | 0x66, 0x0f, 0x20, 0xc3, /* movl %cr0,%ebx */ | ||
342 | 0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60, /* andl $0x60000000,%ebx */ | ||
343 | 0x74, 0x02, /* jz f */ | ||
344 | 0x0f, 0x09, /* wbinvd */ | ||
345 | 0x24, 0x10, /* f: andb $0x10,al */ | ||
346 | 0x66, 0x0f, 0x22, 0xc0 /* movl %eax,%cr0 */ | ||
347 | }; | ||
348 | static const unsigned char jump_to_bios [] = | ||
349 | { | 302 | { |
350 | 0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */ | 303 | void *restart_va; |
351 | }; | 304 | unsigned long restart_pa; |
305 | void (*restart_lowmem)(unsigned int); | ||
306 | u64 *lowmem_gdt; | ||
352 | 307 | ||
353 | /* | ||
354 | * Switch to real mode and then execute the code | ||
355 | * specified by the code and length parameters. | ||
356 | * We assume that length will aways be less that 100! | ||
357 | */ | ||
358 | void machine_real_restart(const unsigned char *code, int length) | ||
359 | { | ||
360 | local_irq_disable(); | 308 | local_irq_disable(); |
361 | 309 | ||
362 | /* Write zero to CMOS register number 0x0f, which the BIOS POST | 310 | /* Write zero to CMOS register number 0x0f, which the BIOS POST |
@@ -384,41 +332,23 @@ void machine_real_restart(const unsigned char *code, int length) | |||
384 | too. */ | 332 | too. */ |
385 | *((unsigned short *)0x472) = reboot_mode; | 333 | *((unsigned short *)0x472) = reboot_mode; |
386 | 334 | ||
387 | /* For the switch to real mode, copy some code to low memory. It has | 335 | /* Patch the GDT in the low memory trampoline */ |
388 | to be in the first 64k because it is running in 16-bit mode, and it | 336 | lowmem_gdt = TRAMPOLINE_SYM(machine_real_restart_gdt); |
389 | has to have the same physical and virtual address, because it turns | 337 | |
390 | off paging. Copy it near the end of the first page, out of the way | 338 | restart_va = TRAMPOLINE_SYM(machine_real_restart_asm); |
391 | of BIOS variables. */ | 339 | restart_pa = virt_to_phys(restart_va); |
392 | memcpy((void *)(0x1000 - sizeof(real_mode_switch) - 100), | 340 | restart_lowmem = (void (*)(unsigned int))restart_pa; |
393 | real_mode_switch, sizeof (real_mode_switch)); | 341 | |
394 | memcpy((void *)(0x1000 - 100), code, length); | 342 | /* GDT[0]: GDT self-pointer */ |
395 | 343 | lowmem_gdt[0] = | |
396 | /* Set up the IDT for real mode. */ | 344 | (u64)(sizeof(machine_real_restart_gdt) - 1) + |
397 | load_idt(&real_mode_idt); | 345 | ((u64)virt_to_phys(lowmem_gdt) << 16); |
398 | 346 | /* GDT[1]: 64K real mode code segment */ | |
399 | /* Set up a GDT from which we can load segment descriptors for real | 347 | lowmem_gdt[1] = |
400 | mode. The GDT is not used in real mode; it is just needed here to | 348 | GDT_ENTRY(0x009b, restart_pa, 0xffff); |
401 | prepare the descriptors. */ | 349 | |
402 | load_gdt(&real_mode_gdt); | 350 | /* Jump to the identity-mapped low memory code */ |
403 | 351 | restart_lowmem(type); | |
404 | /* Load the data segment registers, and thus the descriptors ready for | ||
405 | real mode. The base address of each segment is 0x100, 16 times the | ||
406 | selector value being loaded here. This is so that the segment | ||
407 | registers don't have to be reloaded after switching to real mode: | ||
408 | the values are consistent for real mode operation already. */ | ||
409 | __asm__ __volatile__ ("movl $0x0010,%%eax\n" | ||
410 | "\tmovl %%eax,%%ds\n" | ||
411 | "\tmovl %%eax,%%es\n" | ||
412 | "\tmovl %%eax,%%fs\n" | ||
413 | "\tmovl %%eax,%%gs\n" | ||
414 | "\tmovl %%eax,%%ss" : : : "eax"); | ||
415 | |||
416 | /* Jump to the 16-bit code that we copied earlier. It disables paging | ||
417 | and the cache, switches to real mode, and jumps to the BIOS reset | ||
418 | entry point. */ | ||
419 | __asm__ __volatile__ ("ljmp $0x0008,%0" | ||
420 | : | ||
421 | : "i" ((void *)(0x1000 - sizeof (real_mode_switch) - 100))); | ||
422 | } | 352 | } |
423 | #ifdef CONFIG_APM_MODULE | 353 | #ifdef CONFIG_APM_MODULE |
424 | EXPORT_SYMBOL(machine_real_restart); | 354 | EXPORT_SYMBOL(machine_real_restart); |
@@ -573,7 +503,7 @@ static void native_machine_emergency_restart(void) | |||
573 | 503 | ||
574 | #ifdef CONFIG_X86_32 | 504 | #ifdef CONFIG_X86_32 |
575 | case BOOT_BIOS: | 505 | case BOOT_BIOS: |
576 | machine_real_restart(jump_to_bios, sizeof(jump_to_bios)); | 506 | machine_real_restart(MRR_BIOS); |
577 | 507 | ||
578 | reboot_type = BOOT_KBD; | 508 | reboot_type = BOOT_KBD; |
579 | break; | 509 | break; |
diff --git a/arch/x86/kernel/reboot_32.S b/arch/x86/kernel/reboot_32.S new file mode 100644 index 000000000000..f242356a096e --- /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 | ||
19 | ENTRY(machine_real_restart_asm) | ||
20 | r_base = . | ||
21 | /* Get our own relocated address */ | ||
22 | call 1f | ||
23 | 1: 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 | ||
76 | 1: | ||
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 | ||
87 | 2: | ||
88 | andb $0x10, %al | ||
89 | movl %eax, %cr0 | ||
90 | .byte 0xea /* ljmpw */ | ||
91 | 101: .word 0 /* Offset */ | ||
92 | 102: .word 0 /* Segment */ | ||
93 | |||
94 | bios: | ||
95 | ljmpw $0xf000, $0xfff0 | ||
96 | |||
97 | apm: | ||
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 | |||
106 | END(machine_real_restart_asm) | ||
107 | |||
108 | .balign 16 | ||
109 | /* These must match <asm/reboot.h */ | ||
110 | dispatch_table: | ||
111 | .word bios - r_base | ||
112 | .word apm - r_base | ||
113 | END(dispatch_table) | ||
114 | |||
115 | .balign 16 | ||
116 | machine_real_restart_idt: | ||
117 | .word 0xffff /* Length - real mode default value */ | ||
118 | .long 0 /* Base - real mode default value */ | ||
119 | END(machine_real_restart_idt) | ||
120 | |||
121 | .balign 16 | ||
122 | ENTRY(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) | ||
131 | END(machine_real_restart_gdt) | ||