1/*
2 * Copyright (C) 2018-2022 Intel Corporation.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7/* NOTE:
8 *
9 * MISRA C requires that all unsigned constants should have the suffix 'U'
10 * (e.g. 0xffU), but the assembler may not accept such C-style constants. For
11 * example, binutils 2.26 fails to compile assembly in that case. To work this
12 * around, all unsigned constants must be explicitly spells out in assembly
13 * with a comment tracking the original expression from which the magic
14 * number is calculated. As an example:
15 *
16 *    /* 0x00000668 =
17 *     *    (CR4_DE | CR4_PAE | CR4_MCE | CR4_OSFXSR | CR4_OSXMMEXCPT) *\/
18 *    movl    $0x00000668, %eax
19 *
20 * Make sure that these numbers are updated accordingly if the definition of
21 * the macros involved are changed.
22 */
23
24#include <config.h>
25#include <multiboot_std.h>
26
27/* MULTIBOOT HEADER */
28#define MULTIBOOT_HEADER_FLAGS	MULTIBOOT_HEADER_NEED_MEMINFO
29
30    .extern cpu_primary_save32
31    .extern cpu_primary_save64
32    .section    multiboot_header, "a"
33
34    .align     4
35
36    /* header magic */
37    .long   MULTIBOOT_HEADER_MAGIC
38    /* header flags - flags bit 6 : enable mmap_* */
39    .long   MULTIBOOT_HEADER_FLAGS
40    /* header checksum = -(magic + flags) */
41    .long   -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
42
43#ifdef CONFIG_MULTIBOOT2
44    .align     MULTIBOOT2_HEADER_ALIGN
45mb2_header_start:
46    /* Magic number indicating a Multiboot2 header. */
47    .long   MULTIBOOT2_HEADER_MAGIC
48    /* Architecture: i386. */
49    .long   MULTIBOOT2_ARCHITECTURE_I386
50    /* Multiboot2 header length. */
51    .long   mb2_header_end - mb2_header_start
52    /* Multiboot2 header checksum. */
53    .long   -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT2_ARCHITECTURE_I386 + (mb2_header_end - mb2_header_start))
54
55    /* please be aware that each tag should be 8 bytes aligned */
56    .align     MULTIBOOT2_TAG_ALIGN
57    /*
58     * Request infomation from boot loader, which is supposed to provide th relevant information
59     * specified in the following tags to the image through the MBI if it is available
60     */
61info_req_tag_start:
62    .short  MULTIBOOT2_HEADER_TAG_INFORMATION_REQUEST
63    .short  0
64    .long   info_req_tag_end - info_req_tag_start
65    .long   MULTIBOOT2_TAG_TYPE_MMAP        /* memory map */
66    .long   MULTIBOOT2_TAG_TYPE_MODULE      /* boot modules infomation */
67    .long   MULTIBOOT2_TAG_TYPE_ACPI_NEW    /* a copy of RSDP as defined per ACPI 2.0 or later specification */
68    .long   MULTIBOOT2_TAG_TYPE_EFI64       /* EFI system table, to be passed to guest Linux  */
69    .long   MULTIBOOT2_TAG_TYPE_EFI_MMAP    /* EFI memory map, to be passed to guest Linux */
70info_req_tag_end:
71
72#ifdef CONFIG_RELOC
73    .align  MULTIBOOT2_TAG_ALIGN
74address_tag_start:
75    .short  MULTIBOOT2_HEADER_TAG_ADDRESS
76    .short  0
77    .long   address_tag_end - address_tag_start
78    .long   mb2_header_start  /* address corresponding to the beginning of the Multiboot2 header */
79    .long   ld_ram_start     /* load_addr: load from the binary's beginning */
80    /*
81     * load_end_addr: this includes .bss so that boot loader could reserve the
82     * memory that .bss occupies to avoid placing boot modules or other data in that area.
83     *
84     * However, the boot loader is supposed not to actually load the .bss section because
85     * it's beyond the scope of acrn.bin
86     */
87    .long   ld_ram_end
88    .long   0   /* bss_end_addr, don't ask boot loader to clear .bss */
89address_tag_end:
90
91    .align  MULTIBOOT2_TAG_ALIGN
92entry_address_tag_start:
93    .short  MULTIBOOT2_HEADER_TAG_ENTRY_ADDRESS
94    .short  0
95    .long   entry_address_tag_end - entry_address_tag_start
96    .long   cpu_primary_start_32  /* The address to which the boot loader should jump to start hypervisor */
97entry_address_tag_end:
98
99    .align  MULTIBOOT2_TAG_ALIGN
100relocatable_tag_start:
101    .short  MULTIBOOT2_HEADER_TAG_RELOCATABLE
102    .short  0
103    .long   relocatable_tag_end - relocatable_tag_start
104    .long   CONFIG_HV_RAM_START   /* min_addr */
105    .long   0x80000000   /* max_addr */
106    .long   0x200000     /* image alignment */
107    .long   1            /* preference: lowest possible address */
108relocatable_tag_end:
109#endif	/* CONFIG_RELOC */
110
111    .align     MULTIBOOT2_TAG_ALIGN
112    .short  MULTIBOOT2_HEADER_TAG_END
113    .short  0
114    .long   8
115mb2_header_end:
116#endif	/* CONFIG_MULTIBOOT2 */
117
118    /*
119     * The page tables are aligned to 4KB, which implicitly aligns this section at
120     * 4KB boundary. Put an extra .align here to explicitly state that regardless
121     * the actual length of the multiboot header section, this section will be linked
122     * at offset 0x1000 to the beginning of the target executable.
123     */
124    .align      0x1000
125    .section    entry, "ax"
126    .align      8
127    .code32
128
129    .global     cpu_primary_start_32
130cpu_primary_start_32:
131
132  /*
133   * Calculate the relocation delta between where we were compiled to run
134   * at and where we were actually loaded at.
135   */
136    call    0f
1370:  pop     %esi
138    sub     $0b, %esi
139
140    /* save the MULTBOOT magic number & MBI */
141    movl    %eax, boot_regs(%esi)
142    movl    %ebx, (boot_regs+4)(%esi)
143
144    /* Disable interrupts */
145    cli
146
147    /* Clear direction flag */
148    cld
149
150    /* detect whether it is in long mode
151     *
152     *     0xc0000080 = MSR_IA32_EFER
153     */
154    movl    $0xc0000080, %ecx
155    rdmsr
156    /* 0x400 = MSR_IA32_EFER_LMA_BIT */
157    test     $0x400, %eax
158
159    /* jump to 64bit entry if it is already in long mode */
160    jne      primary_start_long_mode
161
162    /* Disable paging */
163    mov     %cr0, %ebx
164    /* 0x7fffffff = ~CR0_PG */
165    andl    $0x7fffffff, %ebx
166    mov     %ebx, %cr0
167
168    /* Set DE, PAE, MCE and OS support bits in CR4
169     * 0x00000668 =
170     *    (CR4_DE | CR4_PAE | CR4_MCE | CR4_OSFXSR | CR4_OSXMMEXCPT) */
171    movl    $0x00000668, %eax
172    mov     %eax, %cr4
173
174    /* fixup page table pointers with relocation delta */
175    addl    %esi, cpu_primary32_pdpt_addr(%esi)
176    addl    %esi, (cpu_primary32_pdpt_addr+8)(%esi)
177    addl    %esi, (cpu_primary32_pdpt_addr+16)(%esi)
178    addl    %esi, (cpu_primary32_pdpt_addr+24)(%esi)
179
180    /* Set CR3 to PML4 table address */
181    movl    $cpu_boot32_page_tables_start, %edi
182    addl    %esi, %edi
183    addl    %esi, (%edi)
184    mov     %edi, %cr3
185
186    /* Set LME bit in EFER */
187
188    /* 0xc0000080 = MSR_IA32_EFER */
189    movl    $0xc0000080, %ecx
190    rdmsr
191    /* 0x00000100 = MSR_IA32_EFER_LME_BIT */
192    orl     $0x00000100, %eax
193    wrmsr
194
195    /* Enable paging, protection, numeric error and co-processor
196       monitoring in CR0 to enter long mode */
197    mov     %cr0, %ebx
198    /* 0x80000023 = (CR0_PG | CR0_PE | CR0_MP | CR0_NE) */
199    orl     $0x80000023, %ebx
200    mov     %ebx, %cr0
201
202    /* Load temportary GDT pointer value */
203    mov     $cpu_primary32_gdt_ptr, %ebx
204    addl    %esi, %ebx
205    addl    %esi, 2(%ebx)
206    lgdt    (%ebx)
207
208    /* Perform a long jump based to start executing in 64-bit mode */
209    movl    $jmpbuf_32, %eax
210    addl    %esi, %eax
211    addl    %esi, (%eax)
212    ljmp    *(%eax)
213
214jmpbuf_32:
215    .long   primary_start_long_mode
216    /* 0x0008 = HOST_GDT_RING0_CODE_SEL */
217    .word 0x0008
218
219.code64
220primary_start_long_mode:
221
222    /* Initialize temporary stack pointer, size = 0x1000 */
223    lea     stack_for_boot(%rip), %rsp
224    /* 16 = CPU_STACK_ALIGN */
225    and     $(~(16 - 1)),%rsp
226
227    /*
228     * Fix up the .rela sections
229     * Notes: this includes the fixup to IDT tables and temporary
230     *     page tables
231     */
232    call relocate
233
234    call    0f
2350:  pop     %rsi
236    sub     $0b, %rsi    /* relocation delta */
237
238    /* Load temportary GDT pointer value */
239    lea     cpu_primary64_gdt_ptr(%rip), %rbx
240    addq    %rsi, 2(%rbx)
241    lgdt    (%ebx)
242
243    /* Set the correct long jump address */
244    lea     jmpbuf_64(%rip), %rax
245    lea     after(%rip), %rbx
246    mov     %rbx, (%rax)
247    rex.w ljmp  *(%rax)
248jmpbuf_64: .quad 0
249	/* 0x0008 = HOST_GDT_RING0_CODE_SEL */
250        .word 0x0008
251
252after:
253    /* 0x10 = HOST_GDT_RING0_DATA_SEL*/
254    movl    $0x10,%eax
255    mov     %eax,%ss  // Was 32bit POC Stack
256    mov     %eax,%ds  // Was 32bit POC Data
257    mov     %eax,%es  // Was 32bit POC Data
258    mov     %eax,%fs  // Was 32bit POC Data
259    mov     %eax,%gs  // Was 32bit POC CLS
260
261   /* continue with chipset level initialization */
262   call     init_primary_pcpu
263
264loop:
265    jmp loop
266
267    .align  4
268    .global boot_regs
269boot_regs:
270    .long   0x00000000
271    .long   0x00000000
272
273    /* GDT table */
274    .align  4
275cpu_primary32_gdt:
276    .quad   0x0000000000000000
277    .quad   0x00af9b000000ffff
278    .quad   0x00cf93000000ffff
279cpu_primary32_gdt_end:
280
281/* GDT pointer */
282    .align  2
283cpu_primary32_gdt_ptr:
284    .short  (cpu_primary32_gdt_end - cpu_primary32_gdt) - 1
285    .quad   cpu_primary32_gdt
286
287cpu_primary64_gdt_ptr:
288    .short  (cpu_primary32_gdt_end - cpu_primary32_gdt) - 1
289    .quad   cpu_primary32_gdt
290
291/* PML4, PDPT, and PD tables initialized to map first 4 GBytes of memory */
292    /*0x1000 = PAGE_SIZE*/
293    .align  0x1000
294    .global cpu_boot32_page_tables_start
295cpu_boot32_page_tables_start:
296    /* 0x3 = (PAGE_PRESENT | PAGE_RW) */
297    .quad   cpu_primary32_pdpt_addr + 0x3
298    /*0x1000 = PAGE_SIZE*/
299    .align  0x1000
300cpu_primary32_pdpt_addr:
301    address = 0
302    .rept   4
303    /* 0x3 = (PAGE_PRESENT | PAGE_RW) */
304    .quad   cpu_primary32_pdt_addr + address + 0x3
305    /*0x1000 = PAGE_SIZE*/
306    address = address + 0x1000
307    .endr
308    /*0x1000 = PAGE_SIZE*/
309    .align  0x1000
310cpu_primary32_pdt_addr:
311    address = 0
312    .rept  2048
313    /* 0x83 = (PAGE_PSE | PAGE_PRESENT | PAGE_RW) */
314    .quad  address + 0x83
315    address = address + 0x200000
316    .endr
317