1/*
2 * Trampoline code relocated to low memory.
3 *
4 * Care must taken when referencing symbols: they live in the relocated
5 * trampoline and in the hypervisor binary. The hypervisor symbols can either
6 * be accessed by their virtual address or by the physical address. When
7 * using the physical address eventually the physical start address of the
8 * hypervisor must be taken into account: after early boot the hypervisor
9 * will copy itself to high memory and writes its physical start address to
10 * trampoline_xen_phys_start in the low memory trampoline copy.
11 *
12 * Parts of the trampoline are needed for early boot only, while some other
13 * parts are needed as long as the hypervisor is active (e.g. wakeup code
14 * after suspend, bringup code for secondary cpus). The permanent parts should
15 * not reference any temporary low memory trampoline parts as those parts are
16 * not guaranteed to persist.
17 */
18
19/* NB. bootsym() is only usable in real mode, or via BOOT_PSEUDORM_DS. */
20#undef bootsym
21#define bootsym(s) ((s)-trampoline_start)
22
23#define bootsym_rel(sym, off, opnd...)     \
24        bootsym(sym),##opnd;               \
25111:;                                      \
26        .pushsection .trampoline_rel, "a"; \
27        .long 111b - (off) - .;            \
28        .popsection
29
30#define bootsym_segrel(sym, off)           \
31        $0,$bootsym(sym);                  \
32111:;                                      \
33        .pushsection .trampoline_seg, "a"; \
34        .long 111b - (off) - .;            \
35        .popsection
36
37/* Start of the permanent trampoline code. */
38
39        .code16
40
41GLOBAL(trampoline_realmode_entry)
42        mov     %cs,%ax
43        mov     %ax,%ds
44        movb    $0xA5,bootsym(trampoline_cpu_started)
45        cld
46        cli
47        lidt    bootsym(idt_48)
48        lgdt    bootsym(gdt_48)
49        mov     $1,%bl                    # EBX != 0 indicates we are an AP
50        xor     %ax, %ax
51        inc     %ax
52        lmsw    %ax                       # CR0.PE = 1 (enter protected mode)
53        ljmpl   $BOOT_CS32,$bootsym_rel(trampoline_protmode_entry,6)
54
55        .balign 8
56        .word   0
57idt_48: .word   0, 0, 0 # base = limit = 0
58        .word   0
59gdt_48: .word   6*8-1
60        .long   bootsym_rel(trampoline_gdt,4)
61
62trampoline_gdt:
63        /* 0x0000: unused */
64        .quad   0x0000000000000000
65        /* 0x0008: ring 0 code, 32-bit mode */
66        .quad   0x00cf9a000000ffff
67        /* 0x0010: ring 0 code, 64-bit mode */
68        .quad   0x00af9a000000ffff
69        /* 0x0018: ring 0 data */
70        .quad   0x00cf92000000ffff
71        /* 0x0020: real-mode code @ BOOT_TRAMPOLINE */
72        .long   0x0000ffff
73        .long   0x00009a00
74        /* 0x0028: real-mode data @ BOOT_TRAMPOLINE */
75        .long   0x0000ffff
76        .long   0x00009200
77        /*
78         * 0x0030: ring 0 Xen data, 16 MiB size, base
79         * address is computed at runtime.
80         */
81        .quad   0x00c0920000000fff
82
83        .pushsection .trampoline_rel, "a"
84        .long   trampoline_gdt + BOOT_PSEUDORM_CS + 2 - .
85        .long   trampoline_gdt + BOOT_PSEUDORM_DS + 2 - .
86        .popsection
87
88GLOBAL(trampoline_misc_enable_off)
89        .quad   0
90
91GLOBAL(cpuid_ext_features)
92        .long   0
93
94GLOBAL(trampoline_xen_phys_start)
95        .long   0
96
97GLOBAL(trampoline_cpu_started)
98        .byte   0
99
100        .code32
101trampoline_protmode_entry:
102        /* Set up a few descriptors: on entry only CS is guaranteed good. */
103        mov     $BOOT_DS,%eax
104        mov     %eax,%ds
105        mov     %eax,%es
106
107        /* Set up FPU. */
108        fninit
109
110        /* Initialise CR4. */
111        mov     $X86_CR4_PAE,%ecx
112        mov     %ecx,%cr4
113
114        /* Load pagetable base register. */
115        mov     $sym_offs(idle_pg_table),%eax
116        add     bootsym_rel(trampoline_xen_phys_start,4,%eax)
117        mov     %eax,%cr3
118
119        /* Adjust IA32_MISC_ENABLE if needed (for NX enabling below). */
120        mov     bootsym_rel(trampoline_misc_enable_off,4,%esi)
121        mov     bootsym_rel(trampoline_misc_enable_off+4,4,%edi)
122        mov     %esi,%eax
123        or      %edi,%eax
124        jz      1f
125        mov     $MSR_IA32_MISC_ENABLE,%ecx
126        rdmsr
127        not     %esi
128        not     %edi
129        and     %esi,%eax
130        and     %edi,%edx
131        wrmsr
1321:
133
134        /* Set up EFER (Extended Feature Enable Register). */
135        mov     bootsym_rel(cpuid_ext_features,4,%edi)
136        movl    $MSR_EFER,%ecx
137        rdmsr
138        or      $EFER_LME|EFER_SCE,%eax   /* Long Mode + SYSCALL/SYSRET */
139        bt      $cpufeat_bit(X86_FEATURE_NX),%edi /* No Execute? */
140        jnc     1f
141        btsl    $_EFER_NX,%eax  /* No Execute     */
1421:      wrmsr
143
144        mov     $(X86_CR0_PG | X86_CR0_AM | X86_CR0_WP | X86_CR0_NE |\
145                  X86_CR0_ET | X86_CR0_MP | X86_CR0_PE), %eax
146        mov     %eax,%cr0
147        jmp     1f
1481:
149
150        /* Now in compatibility mode. Long-jump into 64-bit mode. */
151        ljmp    $BOOT_CS64,$bootsym_rel(start64,6)
152
153        .code64
154start64:
155        /* Jump to high mappings. */
156        movabs  $__high_start,%rax
157        jmpq    *%rax
158
159#include "wakeup.S"
160
161/* The first page of trampoline is permanent, the rest boot-time only. */
162/* Reuse the boot trampoline on the 1st trampoline page as stack for wakeup. */
163        .equ    wakeup_stack, trampoline_start + PAGE_SIZE
164        .global wakeup_stack
165
166/* From here on early boot only. */
167
168        .code32
169trampoline_boot_cpu_entry:
170        cmpb    $0,bootsym_rel(skip_realmode,5)
171        jnz     .Lskip_realmode
172
173        /* Load pseudo-real-mode segments. */
174        mov     $BOOT_PSEUDORM_DS,%eax
175        mov     %eax,%ds
176        mov     %eax,%es
177        mov     %eax,%fs
178        mov     %eax,%gs
179        mov     %eax,%ss
180
181        /* Switch to pseudo-rm CS, enter real mode, and flush insn queue. */
182        mov     %cr0,%eax
183        dec     %eax
184        ljmp    $BOOT_PSEUDORM_CS,$bootsym(1f)
185        .code16
1861:      mov     %eax,%cr0                 # CR0.PE = 0 (leave protected mode)
187
188        /* Load proper real-mode values into %cs, %ds, %es and %ss. */
189        ljmp    bootsym_segrel(1f,2)
1901:      mov     %cs,%ax
191        mov     %ax,%ds
192        mov     %ax,%es
193        mov     %ax,%ss
194
195        /* Initialise stack pointer and IDT, and enable irqs. */
196        xor     %esp,%esp
197        lidt    bootsym(rm_idt)
198        sti
199
200        /*
201         * Declare that our target operating mode is long mode.
202         * Initialise 32-bit registers since some buggy BIOSes depend on it.
203         */
204        xor     %ecx,%ecx
205        xor     %edx,%edx
206        xor     %esi,%esi
207        xor     %edi,%edi
208        xor     %ebp,%ebp
209        movl    $0xec00,%eax      # declare target operating mode
210        movl    $0x0002,%ebx      # long mode
211        int     $0x15
212
213        /*
214         * Do real-mode work:
215         *  1. Get memory map.
216         *  2. Get Enhanced Disk Drive (EDD) information.
217         *  3. Set video mode.
218         *  4. Get keyboard shift flags.
219         */
220        call    get_memory_map
221        call    get_edd
222#ifdef CONFIG_VIDEO
223        call    video
224#endif
225
226        mov     $0x0200,%ax
227        int     $0x16
228        mov     %al,bootsym(kbd_shift_flags)
229
230        /* Disable irqs before returning to protected mode. */
231        cli
232
233        /* Reset GDT and IDT. Some BIOSes clobber GDTR. */
234        lidt    bootsym(idt_48)
235        lgdt    bootsym(gdt_48)
236
237        /* Enter protected mode, and flush insn queue. */
238        xor     %ax,%ax
239        inc     %ax
240        lmsw    %ax                       # CR0.PE = 1 (enter protected mode)
241
242        /* Load proper protected-mode values into all segment registers. */
243        ljmpl   $BOOT_CS32,$bootsym_rel(1f,6)
244        .code32
2451:      mov     $BOOT_DS,%eax
246        mov     %eax,%ds
247        mov     %eax,%es
248        mov     %eax,%fs
249        mov     %eax,%gs
250        mov     %eax,%ss
251
252.Lskip_realmode:
253        /* EBX == 0 indicates we are the BP (Boot Processor). */
254        xor     %ebx,%ebx
255
256        /* Jump to the common bootstrap entry point. */
257        jmp     trampoline_protmode_entry
258
259#include "video.h"
260
261        .align  2
262/* Keep in sync with cmdline.c:early_boot_opts_t type! */
263early_boot_opts:
264skip_realmode:
265        .byte   0
266opt_edd:
267        .byte   0                               /* edd=on/off/skipmbr */
268opt_edid:
269        .byte   0                               /* EDID parsing option (force/no/default). */
270/* Padding. */
271        .byte   0
272
273#ifdef CONFIG_VIDEO
274GLOBAL(boot_vid_mode)
275        .word   VIDEO_80x25                     /* If we don't run at all, assume basic video mode 3 at 80x25. */
276vesa_size:
277        .word   0,0,0                           /* width x depth x height */
278#endif
279
280GLOBAL(kbd_shift_flags)
281        .byte   0
282
283rm_idt: .word   256*4-1, 0, 0
284
285#include "mem.S"
286#include "edd.S"
287#ifdef CONFIG_VIDEO
288#include "video.S"
289#endif
290