diff options
Diffstat (limited to 'arch/x86/realmode/rm')
-rw-r--r-- | arch/x86/realmode/rm/.gitignore | 3 | ||||
-rw-r--r-- | arch/x86/realmode/rm/Makefile | 82 | ||||
-rw-r--r-- | arch/x86/realmode/rm/bioscall.S | 1 | ||||
-rw-r--r-- | arch/x86/realmode/rm/copy.S | 1 | ||||
-rw-r--r-- | arch/x86/realmode/rm/header.S | 41 | ||||
-rw-r--r-- | arch/x86/realmode/rm/realmode.h | 21 | ||||
-rw-r--r-- | arch/x86/realmode/rm/realmode.lds.S | 76 | ||||
-rw-r--r-- | arch/x86/realmode/rm/reboot_32.S | 132 | ||||
-rw-r--r-- | arch/x86/realmode/rm/regs.c | 1 | ||||
-rw-r--r-- | arch/x86/realmode/rm/stack.S | 19 | ||||
-rw-r--r-- | arch/x86/realmode/rm/trampoline_32.S | 74 | ||||
-rw-r--r-- | arch/x86/realmode/rm/trampoline_64.S | 153 | ||||
-rw-r--r-- | arch/x86/realmode/rm/trampoline_common.S | 7 | ||||
-rw-r--r-- | arch/x86/realmode/rm/video-bios.c | 1 | ||||
-rw-r--r-- | arch/x86/realmode/rm/video-mode.c | 1 | ||||
-rw-r--r-- | arch/x86/realmode/rm/video-vesa.c | 1 | ||||
-rw-r--r-- | arch/x86/realmode/rm/video-vga.c | 1 | ||||
-rw-r--r-- | arch/x86/realmode/rm/wakemain.c | 82 | ||||
-rw-r--r-- | arch/x86/realmode/rm/wakeup.h | 40 | ||||
-rw-r--r-- | arch/x86/realmode/rm/wakeup_asm.S | 177 |
20 files changed, 914 insertions, 0 deletions
diff --git a/arch/x86/realmode/rm/.gitignore b/arch/x86/realmode/rm/.gitignore new file mode 100644 index 000000000000..b6ed3a2555cb --- /dev/null +++ b/arch/x86/realmode/rm/.gitignore | |||
@@ -0,0 +1,3 @@ | |||
1 | pasyms.h | ||
2 | realmode.lds | ||
3 | realmode.relocs | ||
diff --git a/arch/x86/realmode/rm/Makefile b/arch/x86/realmode/rm/Makefile new file mode 100644 index 000000000000..5b84a2d30888 --- /dev/null +++ b/arch/x86/realmode/rm/Makefile | |||
@@ -0,0 +1,82 @@ | |||
1 | # | ||
2 | # arch/x86/realmode/Makefile | ||
3 | # | ||
4 | # This file is subject to the terms and conditions of the GNU General Public | ||
5 | # License. See the file "COPYING" in the main directory of this archive | ||
6 | # for more details. | ||
7 | # | ||
8 | # | ||
9 | |||
10 | always := realmode.bin realmode.relocs | ||
11 | |||
12 | wakeup-objs := wakeup_asm.o wakemain.o video-mode.o | ||
13 | wakeup-objs += copy.o bioscall.o regs.o | ||
14 | # The link order of the video-*.o modules can matter. In particular, | ||
15 | # video-vga.o *must* be listed first, followed by video-vesa.o. | ||
16 | # Hardware-specific drivers should follow in the order they should be | ||
17 | # probed, and video-bios.o should typically be last. | ||
18 | wakeup-objs += video-vga.o | ||
19 | wakeup-objs += video-vesa.o | ||
20 | wakeup-objs += video-bios.o | ||
21 | |||
22 | realmode-y += header.o | ||
23 | realmode-y += trampoline_$(BITS).o | ||
24 | realmode-y += stack.o | ||
25 | realmode-$(CONFIG_X86_32) += reboot_32.o | ||
26 | realmode-$(CONFIG_ACPI_SLEEP) += $(wakeup-objs) | ||
27 | |||
28 | targets += $(realmode-y) | ||
29 | |||
30 | REALMODE_OBJS = $(addprefix $(obj)/,$(realmode-y)) | ||
31 | |||
32 | sed-pasyms := -n -r -e 's/^([0-9a-fA-F]+) [ABCDGRSTVW] (.+)$$/pa_\2 = \2;/p' | ||
33 | |||
34 | quiet_cmd_pasyms = PASYMS $@ | ||
35 | cmd_pasyms = $(NM) $(filter-out FORCE,$^) | \ | ||
36 | sed $(sed-pasyms) | sort | uniq > $@ | ||
37 | |||
38 | targets += pasyms.h | ||
39 | $(obj)/pasyms.h: $(REALMODE_OBJS) FORCE | ||
40 | $(call if_changed,pasyms) | ||
41 | |||
42 | targets += realmode.lds | ||
43 | $(obj)/realmode.lds: $(obj)/pasyms.h | ||
44 | |||
45 | LDFLAGS_realmode.elf := --emit-relocs -T | ||
46 | CPPFLAGS_realmode.lds += -P -C -I$(obj) | ||
47 | |||
48 | targets += realmode.elf | ||
49 | $(obj)/realmode.elf: $(obj)/realmode.lds $(REALMODE_OBJS) FORCE | ||
50 | $(call if_changed,ld) | ||
51 | |||
52 | OBJCOPYFLAGS_realmode.bin := -O binary | ||
53 | |||
54 | targets += realmode.bin | ||
55 | $(obj)/realmode.bin: $(obj)/realmode.elf $(obj)/realmode.relocs | ||
56 | $(call if_changed,objcopy) | ||
57 | |||
58 | quiet_cmd_relocs = RELOCS $@ | ||
59 | cmd_relocs = arch/x86/tools/relocs --realmode $< > $@ | ||
60 | |||
61 | targets += realmode.relocs | ||
62 | $(obj)/realmode.relocs: $(obj)/realmode.elf FORCE | ||
63 | $(call if_changed,relocs) | ||
64 | |||
65 | # --------------------------------------------------------------------------- | ||
66 | |||
67 | # How to compile the 16-bit code. Note we always compile for -march=i386, | ||
68 | # that way we can complain to the user if the CPU is insufficient. | ||
69 | KBUILD_CFLAGS := $(LINUXINCLUDE) -m32 -g -Os -D_SETUP -D__KERNEL__ -D_WAKEUP \ | ||
70 | -I$(srctree)/arch/x86/boot \ | ||
71 | -DDISABLE_BRANCH_PROFILING \ | ||
72 | -Wall -Wstrict-prototypes \ | ||
73 | -march=i386 -mregparm=3 \ | ||
74 | -include $(srctree)/$(src)/../../boot/code16gcc.h \ | ||
75 | -fno-strict-aliasing -fomit-frame-pointer \ | ||
76 | $(call cc-option, -ffreestanding) \ | ||
77 | $(call cc-option, -fno-toplevel-reorder,\ | ||
78 | $(call cc-option, -fno-unit-at-a-time)) \ | ||
79 | $(call cc-option, -fno-stack-protector) \ | ||
80 | $(call cc-option, -mpreferred-stack-boundary=2) | ||
81 | KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__ | ||
82 | GCOV_PROFILE := n | ||
diff --git a/arch/x86/realmode/rm/bioscall.S b/arch/x86/realmode/rm/bioscall.S new file mode 100644 index 000000000000..16162d197918 --- /dev/null +++ b/arch/x86/realmode/rm/bioscall.S | |||
@@ -0,0 +1 @@ | |||
#include "../../boot/bioscall.S" | |||
diff --git a/arch/x86/realmode/rm/copy.S b/arch/x86/realmode/rm/copy.S new file mode 100644 index 000000000000..b785e6f38fdd --- /dev/null +++ b/arch/x86/realmode/rm/copy.S | |||
@@ -0,0 +1 @@ | |||
#include "../../boot/copy.S" | |||
diff --git a/arch/x86/realmode/rm/header.S b/arch/x86/realmode/rm/header.S new file mode 100644 index 000000000000..fadf48378ada --- /dev/null +++ b/arch/x86/realmode/rm/header.S | |||
@@ -0,0 +1,41 @@ | |||
1 | /* | ||
2 | * Real-mode blob header; this should match realmode.h and be | ||
3 | * readonly; for mutable data instead add pointers into the .data | ||
4 | * or .bss sections as appropriate. | ||
5 | */ | ||
6 | |||
7 | #include <linux/linkage.h> | ||
8 | #include <asm/page_types.h> | ||
9 | |||
10 | #include "realmode.h" | ||
11 | |||
12 | .section ".header", "a" | ||
13 | |||
14 | .balign 16 | ||
15 | GLOBAL(real_mode_header) | ||
16 | .long pa_text_start | ||
17 | .long pa_ro_end | ||
18 | /* SMP trampoline */ | ||
19 | .long pa_trampoline_start | ||
20 | .long pa_trampoline_status | ||
21 | .long pa_trampoline_header | ||
22 | #ifdef CONFIG_X86_64 | ||
23 | .long pa_trampoline_pgd; | ||
24 | #endif | ||
25 | /* ACPI S3 wakeup */ | ||
26 | #ifdef CONFIG_ACPI_SLEEP | ||
27 | .long pa_wakeup_start | ||
28 | .long pa_wakeup_header | ||
29 | #endif | ||
30 | /* APM/BIOS reboot */ | ||
31 | #ifdef CONFIG_X86_32 | ||
32 | .long pa_machine_real_restart_asm | ||
33 | #endif | ||
34 | END(real_mode_header) | ||
35 | |||
36 | /* End signature, used to verify integrity */ | ||
37 | .section ".signature","a" | ||
38 | .balign 4 | ||
39 | GLOBAL(end_signature) | ||
40 | .long REALMODE_END_SIGNATURE | ||
41 | END(end_signature) | ||
diff --git a/arch/x86/realmode/rm/realmode.h b/arch/x86/realmode/rm/realmode.h new file mode 100644 index 000000000000..d74cff6350ed --- /dev/null +++ b/arch/x86/realmode/rm/realmode.h | |||
@@ -0,0 +1,21 @@ | |||
1 | #ifndef ARCH_X86_REALMODE_RM_REALMODE_H | ||
2 | #define ARCH_X86_REALMODE_RM_REALMODE_H | ||
3 | |||
4 | #ifdef __ASSEMBLY__ | ||
5 | |||
6 | /* | ||
7 | * 16-bit ljmpw to the real_mode_seg | ||
8 | * | ||
9 | * This must be open-coded since gas will choke on using a | ||
10 | * relocatable symbol for the segment portion. | ||
11 | */ | ||
12 | #define LJMPW_RM(to) .byte 0xea ; .word (to), real_mode_seg | ||
13 | |||
14 | #endif /* __ASSEMBLY__ */ | ||
15 | |||
16 | /* | ||
17 | * Signature at the end of the realmode region | ||
18 | */ | ||
19 | #define REALMODE_END_SIGNATURE 0x65a22c82 | ||
20 | |||
21 | #endif /* ARCH_X86_REALMODE_RM_REALMODE_H */ | ||
diff --git a/arch/x86/realmode/rm/realmode.lds.S b/arch/x86/realmode/rm/realmode.lds.S new file mode 100644 index 000000000000..86b2e8d6b1f1 --- /dev/null +++ b/arch/x86/realmode/rm/realmode.lds.S | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * realmode.lds.S | ||
3 | * | ||
4 | * Linker script for the real-mode code | ||
5 | */ | ||
6 | |||
7 | #include <asm/page_types.h> | ||
8 | |||
9 | #undef i386 | ||
10 | |||
11 | OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") | ||
12 | OUTPUT_ARCH(i386) | ||
13 | |||
14 | SECTIONS | ||
15 | { | ||
16 | real_mode_seg = 0; | ||
17 | |||
18 | . = 0; | ||
19 | .header : { | ||
20 | pa_real_mode_base = .; | ||
21 | *(.header) | ||
22 | } | ||
23 | |||
24 | . = ALIGN(4); | ||
25 | .rodata : { | ||
26 | *(.rodata) | ||
27 | *(.rodata.*) | ||
28 | . = ALIGN(16); | ||
29 | video_cards = .; | ||
30 | *(.videocards) | ||
31 | video_cards_end = .; | ||
32 | } | ||
33 | |||
34 | . = ALIGN(PAGE_SIZE); | ||
35 | pa_text_start = .; | ||
36 | .text : { | ||
37 | *(.text) | ||
38 | *(.text.*) | ||
39 | } | ||
40 | |||
41 | .text32 : { | ||
42 | *(.text32) | ||
43 | *(.text32.*) | ||
44 | } | ||
45 | |||
46 | .text64 : { | ||
47 | *(.text64) | ||
48 | *(.text64.*) | ||
49 | } | ||
50 | pa_ro_end = .; | ||
51 | |||
52 | . = ALIGN(PAGE_SIZE); | ||
53 | .data : { | ||
54 | *(.data) | ||
55 | *(.data.*) | ||
56 | } | ||
57 | |||
58 | . = ALIGN(128); | ||
59 | .bss : { | ||
60 | *(.bss*) | ||
61 | } | ||
62 | |||
63 | /* End signature for integrity checking */ | ||
64 | . = ALIGN(4); | ||
65 | .signature : { | ||
66 | *(.signature) | ||
67 | } | ||
68 | |||
69 | /DISCARD/ : { | ||
70 | *(.note*) | ||
71 | *(.debug*) | ||
72 | *(.eh_frame*) | ||
73 | } | ||
74 | |||
75 | #include "pasyms.h" | ||
76 | } | ||
diff --git a/arch/x86/realmode/rm/reboot_32.S b/arch/x86/realmode/rm/reboot_32.S new file mode 100644 index 000000000000..114044876b3d --- /dev/null +++ b/arch/x86/realmode/rm/reboot_32.S | |||
@@ -0,0 +1,132 @@ | |||
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) | ||
diff --git a/arch/x86/realmode/rm/regs.c b/arch/x86/realmode/rm/regs.c new file mode 100644 index 000000000000..fbb15b9f9ca9 --- /dev/null +++ b/arch/x86/realmode/rm/regs.c | |||
@@ -0,0 +1 @@ | |||
#include "../../boot/regs.c" | |||
diff --git a/arch/x86/realmode/rm/stack.S b/arch/x86/realmode/rm/stack.S new file mode 100644 index 000000000000..867ae87adfae --- /dev/null +++ b/arch/x86/realmode/rm/stack.S | |||
@@ -0,0 +1,19 @@ | |||
1 | /* | ||
2 | * Common heap and stack allocations | ||
3 | */ | ||
4 | |||
5 | #include <linux/linkage.h> | ||
6 | |||
7 | .data | ||
8 | GLOBAL(HEAP) | ||
9 | .long rm_heap | ||
10 | GLOBAL(heap_end) | ||
11 | .long rm_stack | ||
12 | |||
13 | .bss | ||
14 | .balign 16 | ||
15 | GLOBAL(rm_heap) | ||
16 | .space 2048 | ||
17 | GLOBAL(rm_stack) | ||
18 | .space 2048 | ||
19 | GLOBAL(rm_stack_end) | ||
diff --git a/arch/x86/realmode/rm/trampoline_32.S b/arch/x86/realmode/rm/trampoline_32.S new file mode 100644 index 000000000000..c1b2791183e7 --- /dev/null +++ b/arch/x86/realmode/rm/trampoline_32.S | |||
@@ -0,0 +1,74 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Trampoline.S Derived from Setup.S by Linus Torvalds | ||
4 | * | ||
5 | * 4 Jan 1997 Michael Chastain: changed to gnu as. | ||
6 | * | ||
7 | * This is only used for booting secondary CPUs in SMP machine | ||
8 | * | ||
9 | * Entry: CS:IP point to the start of our code, we are | ||
10 | * in real mode with no stack, but the rest of the | ||
11 | * trampoline page to make our stack and everything else | ||
12 | * is a mystery. | ||
13 | * | ||
14 | * We jump into arch/x86/kernel/head_32.S. | ||
15 | * | ||
16 | * On entry to trampoline_start, the processor is in real mode | ||
17 | * with 16-bit addressing and 16-bit data. CS has some value | ||
18 | * and IP is zero. Thus, we load CS to the physical segment | ||
19 | * of the real mode code before doing anything further. | ||
20 | */ | ||
21 | |||
22 | #include <linux/linkage.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <asm/segment.h> | ||
25 | #include <asm/page_types.h> | ||
26 | #include "realmode.h" | ||
27 | |||
28 | .text | ||
29 | .code16 | ||
30 | |||
31 | .balign PAGE_SIZE | ||
32 | ENTRY(trampoline_start) | ||
33 | wbinvd # Needed for NUMA-Q should be harmless for others | ||
34 | |||
35 | LJMPW_RM(1f) | ||
36 | 1: | ||
37 | mov %cs, %ax # Code and data in the same place | ||
38 | mov %ax, %ds | ||
39 | |||
40 | cli # We should be safe anyway | ||
41 | |||
42 | movl tr_start, %eax # where we need to go | ||
43 | |||
44 | movl $0xA5A5A5A5, trampoline_status | ||
45 | # write marker for master knows we're running | ||
46 | |||
47 | /* | ||
48 | * GDT tables in non default location kernel can be beyond 16MB and | ||
49 | * lgdt will not be able to load the address as in real mode default | ||
50 | * operand size is 16bit. Use lgdtl instead to force operand size | ||
51 | * to 32 bit. | ||
52 | */ | ||
53 | lidtl tr_idt # load idt with 0, 0 | ||
54 | lgdtl tr_gdt # load gdt with whatever is appropriate | ||
55 | |||
56 | movw $1, %dx # protected mode (PE) bit | ||
57 | lmsw %dx # into protected mode | ||
58 | |||
59 | ljmpl $__BOOT_CS, $pa_startup_32 | ||
60 | |||
61 | .section ".text32","ax" | ||
62 | .code32 | ||
63 | ENTRY(startup_32) # note: also used from wakeup_asm.S | ||
64 | jmp *%eax | ||
65 | |||
66 | .bss | ||
67 | .balign 8 | ||
68 | GLOBAL(trampoline_header) | ||
69 | tr_start: .space 4 | ||
70 | tr_gdt_pad: .space 2 | ||
71 | tr_gdt: .space 6 | ||
72 | END(trampoline_header) | ||
73 | |||
74 | #include "trampoline_common.S" | ||
diff --git a/arch/x86/realmode/rm/trampoline_64.S b/arch/x86/realmode/rm/trampoline_64.S new file mode 100644 index 000000000000..bb360dc39d21 --- /dev/null +++ b/arch/x86/realmode/rm/trampoline_64.S | |||
@@ -0,0 +1,153 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Trampoline.S Derived from Setup.S by Linus Torvalds | ||
4 | * | ||
5 | * 4 Jan 1997 Michael Chastain: changed to gnu as. | ||
6 | * 15 Sept 2005 Eric Biederman: 64bit PIC support | ||
7 | * | ||
8 | * Entry: CS:IP point to the start of our code, we are | ||
9 | * in real mode with no stack, but the rest of the | ||
10 | * trampoline page to make our stack and everything else | ||
11 | * is a mystery. | ||
12 | * | ||
13 | * On entry to trampoline_start, the processor is in real mode | ||
14 | * with 16-bit addressing and 16-bit data. CS has some value | ||
15 | * and IP is zero. Thus, data addresses need to be absolute | ||
16 | * (no relocation) and are taken with regard to r_base. | ||
17 | * | ||
18 | * With the addition of trampoline_level4_pgt this code can | ||
19 | * now enter a 64bit kernel that lives at arbitrary 64bit | ||
20 | * physical addresses. | ||
21 | * | ||
22 | * If you work on this file, check the object module with objdump | ||
23 | * --full-contents --reloc to make sure there are no relocation | ||
24 | * entries. | ||
25 | */ | ||
26 | |||
27 | #include <linux/linkage.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <asm/pgtable_types.h> | ||
30 | #include <asm/page_types.h> | ||
31 | #include <asm/msr.h> | ||
32 | #include <asm/segment.h> | ||
33 | #include <asm/processor-flags.h> | ||
34 | #include "realmode.h" | ||
35 | |||
36 | .text | ||
37 | .code16 | ||
38 | |||
39 | .balign PAGE_SIZE | ||
40 | ENTRY(trampoline_start) | ||
41 | cli # We should be safe anyway | ||
42 | wbinvd | ||
43 | |||
44 | LJMPW_RM(1f) | ||
45 | 1: | ||
46 | mov %cs, %ax # Code and data in the same place | ||
47 | mov %ax, %ds | ||
48 | mov %ax, %es | ||
49 | mov %ax, %ss | ||
50 | |||
51 | movl $0xA5A5A5A5, trampoline_status | ||
52 | # write marker for master knows we're running | ||
53 | |||
54 | # Setup stack | ||
55 | movl $rm_stack_end, %esp | ||
56 | |||
57 | call verify_cpu # Verify the cpu supports long mode | ||
58 | testl %eax, %eax # Check for return code | ||
59 | jnz no_longmode | ||
60 | |||
61 | /* | ||
62 | * GDT tables in non default location kernel can be beyond 16MB and | ||
63 | * lgdt will not be able to load the address as in real mode default | ||
64 | * operand size is 16bit. Use lgdtl instead to force operand size | ||
65 | * to 32 bit. | ||
66 | */ | ||
67 | |||
68 | lidtl tr_idt # load idt with 0, 0 | ||
69 | lgdtl tr_gdt # load gdt with whatever is appropriate | ||
70 | |||
71 | movw $__KERNEL_DS, %dx # Data segment descriptor | ||
72 | |||
73 | # Enable protected mode | ||
74 | movl $X86_CR0_PE, %eax # protected mode (PE) bit | ||
75 | movl %eax, %cr0 # into protected mode | ||
76 | |||
77 | # flush prefetch and jump to startup_32 | ||
78 | ljmpl $__KERNEL32_CS, $pa_startup_32 | ||
79 | |||
80 | no_longmode: | ||
81 | hlt | ||
82 | jmp no_longmode | ||
83 | #include "../kernel/verify_cpu.S" | ||
84 | |||
85 | .section ".text32","ax" | ||
86 | .code32 | ||
87 | .balign 4 | ||
88 | ENTRY(startup_32) | ||
89 | movl %edx, %ss | ||
90 | addl $pa_real_mode_base, %esp | ||
91 | movl %edx, %ds | ||
92 | movl %edx, %es | ||
93 | movl %edx, %fs | ||
94 | movl %edx, %gs | ||
95 | |||
96 | movl pa_tr_cr4, %eax | ||
97 | movl %eax, %cr4 # Enable PAE mode | ||
98 | |||
99 | # Setup trampoline 4 level pagetables | ||
100 | movl $pa_trampoline_pgd, %eax | ||
101 | movl %eax, %cr3 | ||
102 | |||
103 | # Set up EFER | ||
104 | movl pa_tr_efer, %eax | ||
105 | movl pa_tr_efer + 4, %edx | ||
106 | movl $MSR_EFER, %ecx | ||
107 | wrmsr | ||
108 | |||
109 | # Enable paging and in turn activate Long Mode | ||
110 | movl $(X86_CR0_PG | X86_CR0_WP | X86_CR0_PE), %eax | ||
111 | movl %eax, %cr0 | ||
112 | |||
113 | /* | ||
114 | * At this point we're in long mode but in 32bit compatibility mode | ||
115 | * with EFER.LME = 1, CS.L = 0, CS.D = 1 (and in turn | ||
116 | * EFER.LMA = 1). Now we want to jump in 64bit mode, to do that we use | ||
117 | * the new gdt/idt that has __KERNEL_CS with CS.L = 1. | ||
118 | */ | ||
119 | ljmpl $__KERNEL_CS, $pa_startup_64 | ||
120 | |||
121 | .section ".text64","ax" | ||
122 | .code64 | ||
123 | .balign 4 | ||
124 | ENTRY(startup_64) | ||
125 | # Now jump into the kernel using virtual addresses | ||
126 | jmpq *tr_start(%rip) | ||
127 | |||
128 | .section ".rodata","a" | ||
129 | # Duplicate the global descriptor table | ||
130 | # so the kernel can live anywhere | ||
131 | .balign 16 | ||
132 | .globl tr_gdt | ||
133 | tr_gdt: | ||
134 | .short tr_gdt_end - tr_gdt - 1 # gdt limit | ||
135 | .long pa_tr_gdt | ||
136 | .short 0 | ||
137 | .quad 0x00cf9b000000ffff # __KERNEL32_CS | ||
138 | .quad 0x00af9b000000ffff # __KERNEL_CS | ||
139 | .quad 0x00cf93000000ffff # __KERNEL_DS | ||
140 | tr_gdt_end: | ||
141 | |||
142 | .bss | ||
143 | .balign PAGE_SIZE | ||
144 | GLOBAL(trampoline_pgd) .space PAGE_SIZE | ||
145 | |||
146 | .balign 8 | ||
147 | GLOBAL(trampoline_header) | ||
148 | tr_start: .space 8 | ||
149 | GLOBAL(tr_efer) .space 8 | ||
150 | GLOBAL(tr_cr4) .space 4 | ||
151 | END(trampoline_header) | ||
152 | |||
153 | #include "trampoline_common.S" | ||
diff --git a/arch/x86/realmode/rm/trampoline_common.S b/arch/x86/realmode/rm/trampoline_common.S new file mode 100644 index 000000000000..b1ecdb9692ad --- /dev/null +++ b/arch/x86/realmode/rm/trampoline_common.S | |||
@@ -0,0 +1,7 @@ | |||
1 | .section ".rodata","a" | ||
2 | .balign 16 | ||
3 | tr_idt: .fill 1, 6, 0 | ||
4 | |||
5 | .bss | ||
6 | .balign 4 | ||
7 | GLOBAL(trampoline_status) .space 4 | ||
diff --git a/arch/x86/realmode/rm/video-bios.c b/arch/x86/realmode/rm/video-bios.c new file mode 100644 index 000000000000..848b25aaf11b --- /dev/null +++ b/arch/x86/realmode/rm/video-bios.c | |||
@@ -0,0 +1 @@ | |||
#include "../../boot/video-bios.c" | |||
diff --git a/arch/x86/realmode/rm/video-mode.c b/arch/x86/realmode/rm/video-mode.c new file mode 100644 index 000000000000..2a98b7e2368b --- /dev/null +++ b/arch/x86/realmode/rm/video-mode.c | |||
@@ -0,0 +1 @@ | |||
#include "../../boot/video-mode.c" | |||
diff --git a/arch/x86/realmode/rm/video-vesa.c b/arch/x86/realmode/rm/video-vesa.c new file mode 100644 index 000000000000..413edddb51e5 --- /dev/null +++ b/arch/x86/realmode/rm/video-vesa.c | |||
@@ -0,0 +1 @@ | |||
#include "../../boot/video-vesa.c" | |||
diff --git a/arch/x86/realmode/rm/video-vga.c b/arch/x86/realmode/rm/video-vga.c new file mode 100644 index 000000000000..3085f5c9d288 --- /dev/null +++ b/arch/x86/realmode/rm/video-vga.c | |||
@@ -0,0 +1 @@ | |||
#include "../../boot/video-vga.c" | |||
diff --git a/arch/x86/realmode/rm/wakemain.c b/arch/x86/realmode/rm/wakemain.c new file mode 100644 index 000000000000..91405d515ec6 --- /dev/null +++ b/arch/x86/realmode/rm/wakemain.c | |||
@@ -0,0 +1,82 @@ | |||
1 | #include "wakeup.h" | ||
2 | #include "boot.h" | ||
3 | |||
4 | static void udelay(int loops) | ||
5 | { | ||
6 | while (loops--) | ||
7 | io_delay(); /* Approximately 1 us */ | ||
8 | } | ||
9 | |||
10 | static void beep(unsigned int hz) | ||
11 | { | ||
12 | u8 enable; | ||
13 | |||
14 | if (!hz) { | ||
15 | enable = 0x00; /* Turn off speaker */ | ||
16 | } else { | ||
17 | u16 div = 1193181/hz; | ||
18 | |||
19 | outb(0xb6, 0x43); /* Ctr 2, squarewave, load, binary */ | ||
20 | io_delay(); | ||
21 | outb(div, 0x42); /* LSB of counter */ | ||
22 | io_delay(); | ||
23 | outb(div >> 8, 0x42); /* MSB of counter */ | ||
24 | io_delay(); | ||
25 | |||
26 | enable = 0x03; /* Turn on speaker */ | ||
27 | } | ||
28 | inb(0x61); /* Dummy read of System Control Port B */ | ||
29 | io_delay(); | ||
30 | outb(enable, 0x61); /* Enable timer 2 output to speaker */ | ||
31 | io_delay(); | ||
32 | } | ||
33 | |||
34 | #define DOT_HZ 880 | ||
35 | #define DASH_HZ 587 | ||
36 | #define US_PER_DOT 125000 | ||
37 | |||
38 | /* Okay, this is totally silly, but it's kind of fun. */ | ||
39 | static void send_morse(const char *pattern) | ||
40 | { | ||
41 | char s; | ||
42 | |||
43 | while ((s = *pattern++)) { | ||
44 | switch (s) { | ||
45 | case '.': | ||
46 | beep(DOT_HZ); | ||
47 | udelay(US_PER_DOT); | ||
48 | beep(0); | ||
49 | udelay(US_PER_DOT); | ||
50 | break; | ||
51 | case '-': | ||
52 | beep(DASH_HZ); | ||
53 | udelay(US_PER_DOT * 3); | ||
54 | beep(0); | ||
55 | udelay(US_PER_DOT); | ||
56 | break; | ||
57 | default: /* Assume it's a space */ | ||
58 | udelay(US_PER_DOT * 3); | ||
59 | break; | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | |||
64 | void main(void) | ||
65 | { | ||
66 | /* Kill machine if structures are wrong */ | ||
67 | if (wakeup_header.real_magic != 0x12345678) | ||
68 | while (1) | ||
69 | ; | ||
70 | |||
71 | if (wakeup_header.realmode_flags & 4) | ||
72 | send_morse("...-"); | ||
73 | |||
74 | if (wakeup_header.realmode_flags & 1) | ||
75 | asm volatile("lcallw $0xc000,$3"); | ||
76 | |||
77 | if (wakeup_header.realmode_flags & 2) { | ||
78 | /* Need to call BIOS */ | ||
79 | probe_cards(0); | ||
80 | set_mode(wakeup_header.video_mode); | ||
81 | } | ||
82 | } | ||
diff --git a/arch/x86/realmode/rm/wakeup.h b/arch/x86/realmode/rm/wakeup.h new file mode 100644 index 000000000000..9317e0042f24 --- /dev/null +++ b/arch/x86/realmode/rm/wakeup.h | |||
@@ -0,0 +1,40 @@ | |||
1 | /* | ||
2 | * Definitions for the wakeup data structure at the head of the | ||
3 | * wakeup code. | ||
4 | */ | ||
5 | |||
6 | #ifndef ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H | ||
7 | #define ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H | ||
8 | |||
9 | #ifndef __ASSEMBLY__ | ||
10 | #include <linux/types.h> | ||
11 | |||
12 | /* This must match data at wakeup.S */ | ||
13 | struct wakeup_header { | ||
14 | u16 video_mode; /* Video mode number */ | ||
15 | u32 pmode_entry; /* Protected mode resume point, 32-bit only */ | ||
16 | u16 pmode_cs; | ||
17 | u32 pmode_cr0; /* Protected mode cr0 */ | ||
18 | u32 pmode_cr3; /* Protected mode cr3 */ | ||
19 | u32 pmode_cr4; /* Protected mode cr4 */ | ||
20 | u32 pmode_efer_low; /* Protected mode EFER */ | ||
21 | u32 pmode_efer_high; | ||
22 | u64 pmode_gdt; | ||
23 | u32 pmode_misc_en_low; /* Protected mode MISC_ENABLE */ | ||
24 | u32 pmode_misc_en_high; | ||
25 | u32 pmode_behavior; /* Wakeup routine behavior flags */ | ||
26 | u32 realmode_flags; | ||
27 | u32 real_magic; | ||
28 | u32 signature; /* To check we have correct structure */ | ||
29 | } __attribute__((__packed__)); | ||
30 | |||
31 | extern struct wakeup_header wakeup_header; | ||
32 | #endif | ||
33 | |||
34 | #define WAKEUP_HEADER_OFFSET 8 | ||
35 | #define WAKEUP_HEADER_SIGNATURE 0x51ee1111 | ||
36 | |||
37 | /* Wakeup behavior bits */ | ||
38 | #define WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE 0 | ||
39 | |||
40 | #endif /* ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H */ | ||
diff --git a/arch/x86/realmode/rm/wakeup_asm.S b/arch/x86/realmode/rm/wakeup_asm.S new file mode 100644 index 000000000000..8905166b0bbb --- /dev/null +++ b/arch/x86/realmode/rm/wakeup_asm.S | |||
@@ -0,0 +1,177 @@ | |||
1 | /* | ||
2 | * ACPI wakeup real mode startup stub | ||
3 | */ | ||
4 | #include <linux/linkage.h> | ||
5 | #include <asm/segment.h> | ||
6 | #include <asm/msr-index.h> | ||
7 | #include <asm/page_types.h> | ||
8 | #include <asm/pgtable_types.h> | ||
9 | #include <asm/processor-flags.h> | ||
10 | #include "realmode.h" | ||
11 | #include "wakeup.h" | ||
12 | |||
13 | .code16 | ||
14 | |||
15 | /* This should match the structure in wakeup.h */ | ||
16 | .section ".data", "aw" | ||
17 | |||
18 | .balign 16 | ||
19 | GLOBAL(wakeup_header) | ||
20 | video_mode: .short 0 /* Video mode number */ | ||
21 | pmode_entry: .long 0 | ||
22 | pmode_cs: .short __KERNEL_CS | ||
23 | pmode_cr0: .long 0 /* Saved %cr0 */ | ||
24 | pmode_cr3: .long 0 /* Saved %cr3 */ | ||
25 | pmode_cr4: .long 0 /* Saved %cr4 */ | ||
26 | pmode_efer: .quad 0 /* Saved EFER */ | ||
27 | pmode_gdt: .quad 0 | ||
28 | pmode_misc_en: .quad 0 /* Saved MISC_ENABLE MSR */ | ||
29 | pmode_behavior: .long 0 /* Wakeup behavior flags */ | ||
30 | realmode_flags: .long 0 | ||
31 | real_magic: .long 0 | ||
32 | signature: .long WAKEUP_HEADER_SIGNATURE | ||
33 | END(wakeup_header) | ||
34 | |||
35 | .text | ||
36 | .code16 | ||
37 | |||
38 | .balign 16 | ||
39 | ENTRY(wakeup_start) | ||
40 | cli | ||
41 | cld | ||
42 | |||
43 | LJMPW_RM(3f) | ||
44 | 3: | ||
45 | /* Apparently some dimwit BIOS programmers don't know how to | ||
46 | program a PM to RM transition, and we might end up here with | ||
47 | junk in the data segment descriptor registers. The only way | ||
48 | to repair that is to go into PM and fix it ourselves... */ | ||
49 | movw $16, %cx | ||
50 | lgdtl %cs:wakeup_gdt | ||
51 | movl %cr0, %eax | ||
52 | orb $X86_CR0_PE, %al | ||
53 | movl %eax, %cr0 | ||
54 | ljmpw $8, $2f | ||
55 | 2: | ||
56 | movw %cx, %ds | ||
57 | movw %cx, %es | ||
58 | movw %cx, %ss | ||
59 | movw %cx, %fs | ||
60 | movw %cx, %gs | ||
61 | |||
62 | andb $~X86_CR0_PE, %al | ||
63 | movl %eax, %cr0 | ||
64 | LJMPW_RM(3f) | ||
65 | 3: | ||
66 | /* Set up segments */ | ||
67 | movw %cs, %ax | ||
68 | movw %ax, %ss | ||
69 | movl $rm_stack_end, %esp | ||
70 | movw %ax, %ds | ||
71 | movw %ax, %es | ||
72 | movw %ax, %fs | ||
73 | movw %ax, %gs | ||
74 | |||
75 | lidtl wakeup_idt | ||
76 | |||
77 | /* Clear the EFLAGS */ | ||
78 | pushl $0 | ||
79 | popfl | ||
80 | |||
81 | /* Check header signature... */ | ||
82 | movl signature, %eax | ||
83 | cmpl $WAKEUP_HEADER_SIGNATURE, %eax | ||
84 | jne bogus_real_magic | ||
85 | |||
86 | /* Check we really have everything... */ | ||
87 | movl end_signature, %eax | ||
88 | cmpl $REALMODE_END_SIGNATURE, %eax | ||
89 | jne bogus_real_magic | ||
90 | |||
91 | /* Call the C code */ | ||
92 | calll main | ||
93 | |||
94 | /* Restore MISC_ENABLE before entering protected mode, in case | ||
95 | BIOS decided to clear XD_DISABLE during S3. */ | ||
96 | movl pmode_behavior, %eax | ||
97 | btl $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %eax | ||
98 | jnc 1f | ||
99 | |||
100 | movl pmode_misc_en, %eax | ||
101 | movl pmode_misc_en + 4, %edx | ||
102 | movl $MSR_IA32_MISC_ENABLE, %ecx | ||
103 | wrmsr | ||
104 | 1: | ||
105 | |||
106 | /* Do any other stuff... */ | ||
107 | |||
108 | #ifndef CONFIG_64BIT | ||
109 | /* This could also be done in C code... */ | ||
110 | movl pmode_cr3, %eax | ||
111 | movl %eax, %cr3 | ||
112 | |||
113 | movl pmode_cr4, %ecx | ||
114 | jecxz 1f | ||
115 | movl %ecx, %cr4 | ||
116 | 1: | ||
117 | movl pmode_efer, %eax | ||
118 | movl pmode_efer + 4, %edx | ||
119 | movl %eax, %ecx | ||
120 | orl %edx, %ecx | ||
121 | jz 1f | ||
122 | movl $MSR_EFER, %ecx | ||
123 | wrmsr | ||
124 | 1: | ||
125 | |||
126 | lgdtl pmode_gdt | ||
127 | |||
128 | /* This really couldn't... */ | ||
129 | movl pmode_entry, %eax | ||
130 | movl pmode_cr0, %ecx | ||
131 | movl %ecx, %cr0 | ||
132 | ljmpl $__KERNEL_CS, $pa_startup_32 | ||
133 | /* -> jmp *%eax in trampoline_32.S */ | ||
134 | #else | ||
135 | jmp trampoline_start | ||
136 | #endif | ||
137 | |||
138 | bogus_real_magic: | ||
139 | 1: | ||
140 | hlt | ||
141 | jmp 1b | ||
142 | |||
143 | .section ".rodata","a" | ||
144 | |||
145 | /* | ||
146 | * Set up the wakeup GDT. We set these up as Big Real Mode, | ||
147 | * that is, with limits set to 4 GB. At least the Lenovo | ||
148 | * Thinkpad X61 is known to need this for the video BIOS | ||
149 | * initialization quirk to work; this is likely to also | ||
150 | * be the case for other laptops or integrated video devices. | ||
151 | */ | ||
152 | |||
153 | .balign 16 | ||
154 | GLOBAL(wakeup_gdt) | ||
155 | .word 3*8-1 /* Self-descriptor */ | ||
156 | .long pa_wakeup_gdt | ||
157 | .word 0 | ||
158 | |||
159 | .word 0xffff /* 16-bit code segment @ real_mode_base */ | ||
160 | .long 0x9b000000 + pa_real_mode_base | ||
161 | .word 0x008f /* big real mode */ | ||
162 | |||
163 | .word 0xffff /* 16-bit data segment @ real_mode_base */ | ||
164 | .long 0x93000000 + pa_real_mode_base | ||
165 | .word 0x008f /* big real mode */ | ||
166 | END(wakeup_gdt) | ||
167 | |||
168 | .section ".rodata","a" | ||
169 | .balign 8 | ||
170 | |||
171 | /* This is the standard real-mode IDT */ | ||
172 | .balign 16 | ||
173 | GLOBAL(wakeup_idt) | ||
174 | .word 0xffff /* limit */ | ||
175 | .long 0 /* address */ | ||
176 | .word 0 | ||
177 | END(wakeup_idt) | ||