diff options
author | Jarkko Sakkinen <jarkko.sakkinen@intel.com> | 2012-05-08 14:22:27 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2012-05-08 14:41:50 -0400 |
commit | 5a8c9aebe04a78b069828d364798d5f24c5a42bd (patch) | |
tree | 0c56b496e810fea8b97c31c9a72f49855983234e /arch/x86/realmode/rm | |
parent | 084ee1c641a068bfd1194d545f7dc9ab2043eb35 (diff) |
x86, realmode: Move reboot_32.S to unified realmode code
Migrated reboot_32.S from x86_trampoline to the real-mode
blob.
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@intel.com>
Link: http://lkml.kernel.org/r/1336501366-28617-5-git-send-email-jarkko.sakkinen@intel.com
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'arch/x86/realmode/rm')
-rw-r--r-- | arch/x86/realmode/rm/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/realmode/rm/header.S | 3 | ||||
-rw-r--r-- | arch/x86/realmode/rm/reboot_32.S | 134 |
3 files changed, 138 insertions, 0 deletions
diff --git a/arch/x86/realmode/rm/Makefile b/arch/x86/realmode/rm/Makefile index 7c3f202cbccf..3f851c488593 100644 --- a/arch/x86/realmode/rm/Makefile +++ b/arch/x86/realmode/rm/Makefile | |||
@@ -12,6 +12,7 @@ subdir- := wakeup | |||
12 | always := realmode.bin | 12 | always := realmode.bin |
13 | 13 | ||
14 | realmode-y += header.o | 14 | realmode-y += header.o |
15 | realmode-$(CONFIG_X86_32) += reboot_32.o | ||
15 | 16 | ||
16 | targets += $(realmode-y) | 17 | targets += $(realmode-y) |
17 | 18 | ||
diff --git a/arch/x86/realmode/rm/header.S b/arch/x86/realmode/rm/header.S index 7be17f2c65a3..db21401c0c57 100644 --- a/arch/x86/realmode/rm/header.S +++ b/arch/x86/realmode/rm/header.S | |||
@@ -13,4 +13,7 @@ ENTRY(real_mode_header) | |||
13 | .long pa_text_start | 13 | .long pa_text_start |
14 | .long pa_ro_end | 14 | .long pa_ro_end |
15 | .long pa_end | 15 | .long pa_end |
16 | #ifdef CONFIG_X86_32 | ||
17 | .long pa_machine_real_restart_asm | ||
18 | #endif | ||
16 | END(real_mode_header) | 19 | END(real_mode_header) |
diff --git a/arch/x86/realmode/rm/reboot_32.S b/arch/x86/realmode/rm/reboot_32.S new file mode 100644 index 000000000000..83803c222b4a --- /dev/null +++ b/arch/x86/realmode/rm/reboot_32.S | |||
@@ -0,0 +1,134 @@ | |||
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 ".text32", "ax" | ||
17 | .code32 | ||
18 | .globl machine_real_restart_asm | ||
19 | |||
20 | .balign 16 | ||
21 | 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 | andl $0x60000000, %edx /* If no cache bits -> no wbinvd */ | ||
80 | jz 2f | ||
81 | wbinvd | ||
82 | 2: | ||
83 | andb $0x10, %dl | ||
84 | movl %edx, %cr0 | ||
85 | .byte 0xea /* ljmpw */ | ||
86 | .word 3f /* Offset */ | ||
87 | .word real_mode_seg /* Segment */ | ||
88 | |||
89 | 3: | ||
90 | testb $0, %al | ||
91 | jz bios | ||
92 | |||
93 | apm: | ||
94 | movw $0x1000, %ax | ||
95 | movw %ax, %ss | ||
96 | movw $0xf000, %sp | ||
97 | movw $0x5307, %ax | ||
98 | movw $0x0001, %bx | ||
99 | movw $0x0003, %cx | ||
100 | int $0x15 | ||
101 | /* This should never return... */ | ||
102 | |||
103 | bios: | ||
104 | ljmpw $0xf000, $0xfff0 | ||
105 | |||
106 | .section ".rodata", "a" | ||
107 | .globl machine_real_restart_idt, machine_real_restart_gdt | ||
108 | |||
109 | .balign 16 | ||
110 | machine_real_restart_idt: | ||
111 | .word 0xffff /* Length - real mode default value */ | ||
112 | .long 0 /* Base - real mode default value */ | ||
113 | |||
114 | .balign 16 | ||
115 | machine_real_restart_gdt: | ||
116 | /* Self-pointer */ | ||
117 | .word 0xffff /* Length - real mode default value */ | ||
118 | .long pa_machine_real_restart_gdt | ||
119 | .word 0 | ||
120 | |||
121 | /* | ||
122 | * 16-bit code segment pointing to real_mode_seg | ||
123 | * Selector value 8 | ||
124 | */ | ||
125 | .word 0xffff /* Limit */ | ||
126 | .long 0x9b000000 + pa_real_mode_base | ||
127 | .word 0 | ||
128 | |||
129 | /* | ||
130 | * 16-bit data segment with the selector value 16 = 0x10 and | ||
131 | * base value 0x100; since this is consistent with real mode | ||
132 | * semantics we don't have to reload the segments once CR0.PE = 0. | ||
133 | */ | ||
134 | .quad GDT_ENTRY(0x0093, 0x100, 0xffff) | ||