aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/reboot.h5
-rw-r--r--arch/x86/kernel/Makefile1
-rw-r--r--arch/x86/kernel/apm_32.c12
-rw-r--r--arch/x86/kernel/reboot.c120
-rw-r--r--arch/x86/kernel/reboot_32.S131
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
19void native_machine_crash_shutdown(struct pt_regs *regs); 19void native_machine_crash_shutdown(struct pt_regs *regs);
20void native_machine_shutdown(void); 20void native_machine_shutdown(void);
21void machine_real_restart(const unsigned char *code, int length); 21void 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
23typedef void (*nmi_shootdown_cb)(int, struct die_args*); 26typedef void (*nmi_shootdown_cb)(int, struct die_args*);
24void nmi_shootdown_cpus(nmi_shootdown_cb callback); 27void 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
59obj-y += cpu/ 59obj-y += cpu/
60obj-y += acpi/ 60obj-y += acpi/
61obj-y += reboot.o 61obj-y += reboot.o
62obj-$(CONFIG_X86_32) += reboot_32.o
62obj-$(CONFIG_MCA) += mca_32.o 63obj-$(CONFIG_MCA) += mca_32.o
63obj-$(CONFIG_X86_MSR) += msr.o 64obj-$(CONFIG_X86_MSR) += msr.o
64obj-$(CONFIG_X86_CPUID) += cpuid.o 65obj-$(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
976static void apm_power_off(void) 976static 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}
296core_initcall(reboot_init); 296core_initcall(reboot_init);
297 297
298/* The following code and data reboots the machine by switching to real 298extern const unsigned char machine_real_restart_asm[];
299 mode and jumping to the BIOS reset entry point, as if the CPU has 299extern 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. */
304static const unsigned long long
305real_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
312static const struct desc_ptr 301void machine_real_restart(unsigned int type)
313real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, (long)real_mode_gdt_entries },
314real_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. */
334static 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};
348static 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 */
358void 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
424EXPORT_SYMBOL(machine_real_restart); 354EXPORT_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
19ENTRY(machine_real_restart_asm)
20r_base = .
21 /* Get our own relocated address */
22 call 1f
231: 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
761:
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
872:
88 andb $0x10, %al
89 movl %eax, %cr0
90 .byte 0xea /* ljmpw */
91101: .word 0 /* Offset */
92102: .word 0 /* Segment */
93
94bios:
95 ljmpw $0xf000, $0xfff0
96
97apm:
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
106END(machine_real_restart_asm)
107
108 .balign 16
109 /* These must match <asm/reboot.h */
110dispatch_table:
111 .word bios - r_base
112 .word apm - r_base
113END(dispatch_table)
114
115 .balign 16
116machine_real_restart_idt:
117 .word 0xffff /* Length - real mode default value */
118 .long 0 /* Base - real mode default value */
119END(machine_real_restart_idt)
120
121 .balign 16
122ENTRY(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)
131END(machine_real_restart_gdt)