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
41/*
42 * do_boot_cpu() programs the Startup-IPI to point here.  Due to the SIPI
43 * format, the relocated entrypoint must be 4k aligned.
44 *
45 * It is entered in Real Mode, with %cs = trampoline_realmode_entry >> 4 and
46 * %ip = 0.
47 */
48GLOBAL(trampoline_realmode_entry)
49        mov     %cs,%ax
50        mov     %ax,%ds
51        movb    $0xA5,bootsym(trampoline_cpu_started)
52        cld
53        cli
54        lidt    bootsym(idt_48)
55        lgdt    bootsym(gdt_48)
56        mov     $X86_CR0_PE, %ebx         # EBX != 0 indicates we are an AP
57        mov     %ebx, %cr0                # Alias with CR0.PE for brevity
58
59        ljmpl   $BOOT_CS32,$bootsym_rel(trampoline_protmode_entry,6)
60
61        .code32
62trampoline_protmode_entry:
63        /* Set up a few descriptors: on entry only CS is guaranteed good. */
64        mov     $BOOT_DS,%eax
65        mov     %eax,%ds
66        mov     %eax,%es
67
68        /* Set up FPU. */
69        fninit
70
71        /* Initialise CR4. */
72        mov     $X86_CR4_PAE,%ecx
73        mov     %ecx,%cr4
74
75        /* Load pagetable base register. */
76        mov     $sym_offs(idle_pg_table),%eax
77        add     bootsym_rel(trampoline_xen_phys_start,4,%eax)
78        mov     %eax,%cr3
79
80        /* Adjust IA32_MISC_ENABLE if needed (for NX enabling below). */
81        mov     bootsym_rel(trampoline_misc_enable_off,4,%esi)
82        mov     bootsym_rel(trampoline_misc_enable_off+4,4,%edi)
83        mov     %esi,%eax
84        or      %edi,%eax
85        jz      1f
86        mov     $MSR_IA32_MISC_ENABLE,%ecx
87        rdmsr
88        not     %esi
89        not     %edi
90        and     %esi,%eax
91        and     %edi,%edx
92        wrmsr
931:
94        /* Set up PAT before enabling paging. */
95        mov     $XEN_MSR_PAT & 0xffffffff, %eax
96        mov     $XEN_MSR_PAT >> 32, %edx
97        mov     $MSR_IA32_CR_PAT, %ecx
98        wrmsr
99
100        /* Set up EFER (Extended Feature Enable Register). */
101        movl    $MSR_EFER,%ecx
102        rdmsr
103        or      bootsym_rel(trampoline_efer, 4, %eax)
104        wrmsr
105
106        mov     $(X86_CR0_PG | X86_CR0_AM | X86_CR0_WP | X86_CR0_NE |\
107                  X86_CR0_ET | X86_CR0_MP | X86_CR0_PE), %eax
108        mov     %eax,%cr0
109
110        /* Now in compatibility mode. Long-jump into 64-bit mode. */
111        ljmp    $BOOT_CS64,$bootsym_rel(start64,6)
112
113        .code64
114start64:
115        /* Jump to high mappings. */
116        movabs  $__high_start, %rdi
117        jmpq    *%rdi
118
119#include "video.h"
120#include "wakeup.S"
121
122        .balign 8
123        .word   0
124idt_48: .word   0, 0, 0 # base = limit = 0
125
126trampoline_gdt:
127        .word   0                  /* 0x0000: unused (reused for GDTR) */
128gdt_48:
129        .word   .Ltrampoline_gdt_end - trampoline_gdt - 1
130        .long   bootsym_rel(trampoline_gdt, 4)
131
132        .quad   0x00cf9b000000ffff /* 0x0008: ring 0 code, 32-bit mode */
133        .quad   0x00af9b000000ffff /* 0x0010: ring 0 code, 64-bit mode */
134        .quad   0x00cf93000000ffff /* 0x0018: ring 0 data */
135        .quad   0x00009b000000ffff /* 0x0020: real-mode code @ BOOT_TRAMPOLINE */
136        .quad   0x000093000000ffff /* 0x0028: real-mode data @ BOOT_TRAMPOLINE */
137.Ltrampoline_gdt_end:
138
139        /* Relocations for trampoline Real Mode segments. */
140        .pushsection .trampoline_rel, "a"
141        .long   trampoline_gdt + BOOT_PSEUDORM_CS + 2 - .
142        .long   trampoline_gdt + BOOT_PSEUDORM_DS + 2 - .
143        .popsection
144
145GLOBAL(trampoline_misc_enable_off)
146        .quad   0
147
148/* EFER OR-mask for boot paths.  SCE conditional on PV support, NX added when available. */
149GLOBAL(trampoline_efer)
150        .long   EFER_LME | (EFER_SCE * IS_ENABLED(CONFIG_PV))
151
152GLOBAL(trampoline_xen_phys_start)
153        .long   0
154
155GLOBAL(trampoline_cpu_started)
156        .byte   0
157
158/* The first page of trampoline is permanent, the rest boot-time only. */
159/* Reuse the boot trampoline on the 1st trampoline page as stack for wakeup. */
160        .equ    wakeup_stack, trampoline_start + PAGE_SIZE
161        .global wakeup_stack
162
163/* From here on early boot only. */
164
165        .code32
166trampoline_boot_cpu_entry:
167        cmpb    $0,bootsym_rel(skip_realmode,5)
168        jnz     .Lskip_realmode
169
170        /* Load pseudo-real-mode segments. */
171        mov     $BOOT_PSEUDORM_DS,%eax
172        mov     %eax,%ds
173        mov     %eax,%es
174        mov     %eax,%fs
175        mov     %eax,%gs
176        mov     %eax,%ss
177
178        /* Switch to pseudo-rm CS, enter real mode, and flush insn queue. */
179        mov     %cr0,%eax
180        dec     %eax
181        ljmp    $BOOT_PSEUDORM_CS,$bootsym(1f)
182        .code16
1831:      mov     %eax,%cr0                 # CR0.PE = 0 (leave protected mode)
184
185        /* Load proper real-mode values into %cs, %ds, %es and %ss. */
186        ljmp    bootsym_segrel(1f,2)
1871:      mov     %cs,%ax
188        mov     %ax,%ds
189        mov     %ax,%es
190        mov     %ax,%ss
191
192        /* Initialise stack pointer and IDT, and enable irqs. */
193        xor     %esp,%esp
194        lidt    bootsym(rm_idt)
195        sti
196
197        /*
198         * Declare that our target operating mode is long mode.
199         * Initialise 32-bit registers since some buggy BIOSes depend on it.
200         */
201        xor     %ecx,%ecx
202        xor     %edx,%edx
203        xor     %esi,%esi
204        xor     %edi,%edi
205        xor     %ebp,%ebp
206        movl    $0xec00,%eax      # declare target operating mode
207        movl    $0x0002,%ebx      # long mode
208        int     $0x15
209
210        /*
211         * Do real-mode work:
212         *  1. Get memory map.
213         *  2. Get Enhanced Disk Drive (EDD) information.
214         *  3. Set video mode.
215         *  4. Get keyboard shift flags.
216         */
217        call    get_memory_map
218        call    get_edd
219#ifdef CONFIG_VIDEO
220        call    video
221#endif
222
223        mov     $0x0200,%ax
224        int     $0x16
225        mov     %al,bootsym(kbd_shift_flags)
226
227        /* Disable irqs before returning to protected mode. */
228        cli
229
230        /* Reset GDT and IDT. Some BIOSes clobber GDTR. */
231        lidt    bootsym(idt_48)
232        lgdt    bootsym(gdt_48)
233
234        /* Enter protected mode, and flush insn queue. */
235        mov     $X86_CR0_PE, %eax
236        mov     %eax, %cr0
237
238        /* Load proper protected-mode values into all segment registers. */
239        ljmpl   $BOOT_CS32,$bootsym_rel(1f,6)
240        .code32
2411:      mov     $BOOT_DS,%eax
242        mov     %eax,%ds
243        mov     %eax,%es
244        mov     %eax,%fs
245        mov     %eax,%gs
246        mov     %eax,%ss
247
248.Lskip_realmode:
249        /* EBX == 0 indicates we are the BP (Boot Processor). */
250        xor     %ebx,%ebx
251
252        /* Jump to the common bootstrap entry point. */
253        jmp     trampoline_protmode_entry
254
255        .align  2
256/* Keep in sync with cmdline.c:early_boot_opts_t type! */
257early_boot_opts:
258skip_realmode:
259        .byte   0
260opt_edd:
261        .byte   0                               /* edd=on/off/skipmbr */
262opt_edid:
263        .byte   0                               /* EDID parsing option (force/no/default). */
264/* Padding. */
265        .byte   0
266
267#ifdef CONFIG_VIDEO
268boot_vid_mode:
269        .word   VIDEO_80x25                     /* If we don't run at all, assume basic video mode 3 at 80x25. */
270vesa_size:
271        .word   0,0,0                           /* width x depth x height */
272#endif
273
274GLOBAL(kbd_shift_flags)
275        .byte   0
276
277rm_idt: .word   256*4-1, 0, 0
278
279#include "mem.S"
280#include "edd.S"
281#ifdef CONFIG_VIDEO
282#include "video.S"
283#endif
284