diff options
Diffstat (limited to 'arch/x86/boot/pm.c')
-rw-r--r-- | arch/x86/boot/pm.c | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/arch/x86/boot/pm.c b/arch/x86/boot/pm.c new file mode 100644 index 000000000000..09fb342cc62e --- /dev/null +++ b/arch/x86/boot/pm.c | |||
@@ -0,0 +1,174 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/pm.c | ||
13 | * | ||
14 | * Prepare the machine for transition to protected mode. | ||
15 | */ | ||
16 | |||
17 | #include "boot.h" | ||
18 | #include <asm/segment.h> | ||
19 | |||
20 | /* | ||
21 | * Invoke the realmode switch hook if present; otherwise | ||
22 | * disable all interrupts. | ||
23 | */ | ||
24 | static void realmode_switch_hook(void) | ||
25 | { | ||
26 | if (boot_params.hdr.realmode_swtch) { | ||
27 | asm volatile("lcallw *%0" | ||
28 | : : "m" (boot_params.hdr.realmode_swtch) | ||
29 | : "eax", "ebx", "ecx", "edx"); | ||
30 | } else { | ||
31 | asm volatile("cli"); | ||
32 | outb(0x80, 0x70); /* Disable NMI */ | ||
33 | io_delay(); | ||
34 | } | ||
35 | } | ||
36 | |||
37 | /* | ||
38 | * A zImage kernel is loaded at 0x10000 but wants to run at 0x1000. | ||
39 | * A bzImage kernel is loaded and runs at 0x100000. | ||
40 | */ | ||
41 | static void move_kernel_around(void) | ||
42 | { | ||
43 | /* Note: rely on the compile-time option here rather than | ||
44 | the LOADED_HIGH flag. The Qemu kernel loader unconditionally | ||
45 | sets the loadflags to zero. */ | ||
46 | #ifndef __BIG_KERNEL__ | ||
47 | u16 dst_seg, src_seg; | ||
48 | u32 syssize; | ||
49 | |||
50 | dst_seg = 0x1000 >> 4; | ||
51 | src_seg = 0x10000 >> 4; | ||
52 | syssize = boot_params.hdr.syssize; /* Size in 16-byte paragraphs */ | ||
53 | |||
54 | while (syssize) { | ||
55 | int paras = (syssize >= 0x1000) ? 0x1000 : syssize; | ||
56 | int dwords = paras << 2; | ||
57 | |||
58 | asm volatile("pushw %%es ; " | ||
59 | "pushw %%ds ; " | ||
60 | "movw %1,%%es ; " | ||
61 | "movw %2,%%ds ; " | ||
62 | "xorw %%di,%%di ; " | ||
63 | "xorw %%si,%%si ; " | ||
64 | "rep;movsl ; " | ||
65 | "popw %%ds ; " | ||
66 | "popw %%es" | ||
67 | : "+c" (dwords) | ||
68 | : "r" (dst_seg), "r" (src_seg) | ||
69 | : "esi", "edi"); | ||
70 | |||
71 | syssize -= paras; | ||
72 | dst_seg += paras; | ||
73 | src_seg += paras; | ||
74 | } | ||
75 | #endif | ||
76 | } | ||
77 | |||
78 | /* | ||
79 | * Disable all interrupts at the legacy PIC. | ||
80 | */ | ||
81 | static void mask_all_interrupts(void) | ||
82 | { | ||
83 | outb(0xff, 0xa1); /* Mask all interrupts on the secondary PIC */ | ||
84 | io_delay(); | ||
85 | outb(0xfb, 0x21); /* Mask all but cascade on the primary PIC */ | ||
86 | io_delay(); | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * Reset IGNNE# if asserted in the FPU. | ||
91 | */ | ||
92 | static void reset_coprocessor(void) | ||
93 | { | ||
94 | outb(0, 0xf0); | ||
95 | io_delay(); | ||
96 | outb(0, 0xf1); | ||
97 | io_delay(); | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | * Set up the GDT | ||
102 | */ | ||
103 | #define GDT_ENTRY(flags,base,limit) \ | ||
104 | (((u64)(base & 0xff000000) << 32) | \ | ||
105 | ((u64)flags << 40) | \ | ||
106 | ((u64)(limit & 0x00ff0000) << 32) | \ | ||
107 | ((u64)(base & 0x00ffff00) << 16) | \ | ||
108 | ((u64)(limit & 0x0000ffff))) | ||
109 | |||
110 | struct gdt_ptr { | ||
111 | u16 len; | ||
112 | u32 ptr; | ||
113 | } __attribute__((packed)); | ||
114 | |||
115 | static void setup_gdt(void) | ||
116 | { | ||
117 | /* There are machines which are known to not boot with the GDT | ||
118 | being 8-byte unaligned. Intel recommends 16 byte alignment. */ | ||
119 | static const u64 boot_gdt[] __attribute__((aligned(16))) = { | ||
120 | /* CS: code, read/execute, 4 GB, base 0 */ | ||
121 | [GDT_ENTRY_BOOT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff), | ||
122 | /* DS: data, read/write, 4 GB, base 0 */ | ||
123 | [GDT_ENTRY_BOOT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff), | ||
124 | }; | ||
125 | /* Xen HVM incorrectly stores a pointer to the gdt_ptr, instead | ||
126 | of the gdt_ptr contents. Thus, make it static so it will | ||
127 | stay in memory, at least long enough that we switch to the | ||
128 | proper kernel GDT. */ | ||
129 | static struct gdt_ptr gdt; | ||
130 | |||
131 | gdt.len = sizeof(boot_gdt)-1; | ||
132 | gdt.ptr = (u32)&boot_gdt + (ds() << 4); | ||
133 | |||
134 | asm volatile("lgdtl %0" : : "m" (gdt)); | ||
135 | } | ||
136 | |||
137 | /* | ||
138 | * Set up the IDT | ||
139 | */ | ||
140 | static void setup_idt(void) | ||
141 | { | ||
142 | static const struct gdt_ptr null_idt = {0, 0}; | ||
143 | asm volatile("lidtl %0" : : "m" (null_idt)); | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * Actual invocation sequence | ||
148 | */ | ||
149 | void go_to_protected_mode(void) | ||
150 | { | ||
151 | /* Hook before leaving real mode, also disables interrupts */ | ||
152 | realmode_switch_hook(); | ||
153 | |||
154 | /* Move the kernel/setup to their final resting places */ | ||
155 | move_kernel_around(); | ||
156 | |||
157 | /* Enable the A20 gate */ | ||
158 | if (enable_a20()) { | ||
159 | puts("A20 gate not responding, unable to boot...\n"); | ||
160 | die(); | ||
161 | } | ||
162 | |||
163 | /* Reset coprocessor (IGNNE#) */ | ||
164 | reset_coprocessor(); | ||
165 | |||
166 | /* Mask all interrupts in the PIC */ | ||
167 | mask_all_interrupts(); | ||
168 | |||
169 | /* Actual transition to protected mode... */ | ||
170 | setup_idt(); | ||
171 | setup_gdt(); | ||
172 | protected_mode_jump(boot_params.hdr.code32_start, | ||
173 | (u32)&boot_params + (ds() << 4)); | ||
174 | } | ||