acrn-hypervisor/hypervisor/arch/x86/boot/cpu_primary.S

343 lines
10 KiB
ArmAsm

/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/* NOTE:
*
* MISRA C requires that all unsigned constants should have the suffix 'U'
* (e.g. 0xffU), but the assembler may not accept such C-style constants. For
* example, binutils 2.26 fails to compile assembly in that case. To work this
* around, all unsigned constants must be explicitly spells out in assembly
* with a comment tracking the original expression from which the magic
* number is calculated. As an example:
*
* /* 0x00000668 =
* * (CR4_DE | CR4_PAE | CR4_MCE | CR4_OSFXSR | CR4_OSXMMEXCPT) *\/
* movl $0x00000668, %eax
*
* Make sure that these numbers are updated accordingly if the definition of
* the macros involved are changed.
*/
#include <multiboot.h>
#ifdef CONFIG_MULTIBOOT2
#include <multiboot2.h>
#endif
/* MULTIBOOT HEADER */
#define MULTIBOOT_HEADER_FLAGS MULTIBOOT_HEADER_NEED_MEMINFO
.extern cpu_primary_save32
.extern cpu_primary_save64
.section multiboot_header, "a"
.align 4
/* header magic */
.long MULTIBOOT_HEADER_MAGIC
/* header flags - flags bit 6 : enable mmap_* */
.long MULTIBOOT_HEADER_FLAGS
/* header checksum = -(magic + flags) */
.long -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
#ifdef CONFIG_MULTIBOOT2
.align MULTIBOOT2_HEADER_ALIGN
mb2_header_start:
/* Magic number indicating a Multiboot2 header. */
.long MULTIBOOT2_HEADER_MAGIC
/* Architecture: i386. */
.long MULTIBOOT2_ARCHITECTURE_I386
/* Multiboot2 header length. */
.long mb2_header_end - mb2_header_start
/* Multiboot2 header checksum. */
.long -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT2_ARCHITECTURE_I386 + (mb2_header_end - mb2_header_start))
/* please be aware that each tag should be 8 bytes aligned */
.align MULTIBOOT2_TAG_ALIGN
/*
* Request infomation from boot loader, which is supposed to provide th relevant information
* specified in the following tags to the image through the MBI if it is available
*/
info_req_tag_start:
.short MULTIBOOT2_HEADER_TAG_INFORMATION_REQUEST
.short 0
.long info_req_tag_end - info_req_tag_start
.long MULTIBOOT2_TAG_TYPE_MMAP /* memory map */
.long MULTIBOOT2_TAG_TYPE_MODULE /* boot modules infomation */
.long MULTIBOOT2_TAG_TYPE_ACPI_NEW /* a copy of RSDP as defined per ACPI 2.0 or later specification */
.long MULTIBOOT2_TAG_TYPE_EFI64 /* EFI system table, to be passed to guest Linux */
.long MULTIBOOT2_TAG_TYPE_EFI_MMAP /* EFI memory map, to be passed to guest Linux */
info_req_tag_end:
#ifdef CONFIG_RELOC
.align MULTIBOOT2_TAG_ALIGN
address_tag_start:
.short MULTIBOOT2_HEADER_TAG_ADDRESS
.short 0
.long address_tag_end - address_tag_start
.long mb2_header_start /* address corresponding to the beginning of the Multiboot2 header */
.long _ld_ram_start /* load_addr: load from the binary's beginning */
/*
* load_end_addr: this includes .bss so that boot loader could reserve the
* memory that .bss occupies to avoid placing boot modules or other data in that area.
*
* However, the boot loader is supposed not to actually load the .bss section because
* it's beyond the scope of acrn.bin
*/
.long _ld_ram_end
.long 0 /* bss_end_addr, don't ask boot loader to clear .bss */
address_tag_end:
.align MULTIBOOT2_TAG_ALIGN
entry_address_tag_start:
.short MULTIBOOT2_HEADER_TAG_ENTRY_ADDRESS
.short 0
.long entry_address_tag_end - entry_address_tag_start
.long cpu_primary_start_32 /* The address to which the boot loader should jump to start hypervisor */
entry_address_tag_end:
.align MULTIBOOT2_TAG_ALIGN
relocatable_tag_start:
.short MULTIBOOT2_HEADER_TAG_RELOCATABLE
.short 0
.long relocatable_tag_end - relocatable_tag_start
.long 0x10000000 /* min_addr. TODO: change it to 2MB after fixing the load_addr issue */
.long 0x80000000 /* max_addr */
.long 0x200000 /* image alignment */
.long 1 /* preference: lowest possible address */
relocatable_tag_end:
#endif /* CONFIG_RELOC */
.align MULTIBOOT2_TAG_ALIGN
.short MULTIBOOT2_HEADER_TAG_END
.short 0
.long 8
mb2_header_end:
#endif /* CONFIG_MULTIBOOT2 */
/*
* The page tables are aligned to 4KB, which implicitly aligns this section at
* 4KB boundary. Put an extra .align here to explicitly state that regardless
* the actual length of the multiboot header section, this section will be linked
* at offset 0x1000 to the beginning of the target executable.
*/
.align 0x1000
.section entry, "ax"
.align 8
.code32
.global cpu_primary_start_32
cpu_primary_start_32:
/*
* Calculate the relocation delta between where we were compiled to run
* at and where we were actually loaded at.
*/
call 0f
0: pop %esi
sub $0b, %esi
/* save the MULTBOOT magic number & MBI */
movl %eax, boot_regs(%esi)
movl %ebx, (boot_regs+4)(%esi)
/* Disable interrupts */
cli
/* Clear direction flag */
cld
/* detect whether it is in long mode
*
* 0xc0000080 = MSR_IA32_EFER
*/
movl $0xc0000080, %ecx
rdmsr
/* 0x400 = MSR_IA32_EFER_LMA_BIT */
test $0x400, %eax
/* jump to 64bit entry if it is already in long mode */
jne cpu_primary_start_64
/* Disable paging */
mov %cr0, %ebx
/* 0x7fffffff = ~CR0_PG */
andl $0x7fffffff, %ebx
mov %ebx, %cr0
/* Set DE, PAE, MCE and OS support bits in CR4
* 0x00000668 =
* (CR4_DE | CR4_PAE | CR4_MCE | CR4_OSFXSR | CR4_OSXMMEXCPT) */
movl $0x00000668, %eax
mov %eax, %cr4
/* fixup page table pointers with relocation delta */
addl %esi, cpu_primary32_pdpt_addr(%esi)
addl %esi, (cpu_primary32_pdpt_addr+8)(%esi)
addl %esi, (cpu_primary32_pdpt_addr+16)(%esi)
addl %esi, (cpu_primary32_pdpt_addr+24)(%esi)
/* Set CR3 to PML4 table address */
movl $cpu_boot32_page_tables_start, %edi
addl %esi, %edi
addl %esi, (%edi)
mov %edi, %cr3
/* Set LME bit in EFER */
/* 0xc0000080 = MSR_IA32_EFER */
movl $0xc0000080, %ecx
rdmsr
/* 0x00000100 = MSR_IA32_EFER_LME_BIT */
orl $0x00000100, %eax
wrmsr
/* Enable paging, protection, numeric error and co-processor
monitoring in CR0 to enter long mode */
mov %cr0, %ebx
/* 0x80000023 = (CR0_PG | CR0_PE | CR0_MP | CR0_NE) */
orl $0x80000023, %ebx
mov %ebx, %cr0
/* Load temportary GDT pointer value */
mov $cpu_primary32_gdt_ptr, %ebx
addl %esi, %ebx
addl %esi, 2(%ebx)
lgdt (%ebx)
/* Perform a long jump based to start executing in 64-bit mode */
movl $jmpbuf_32, %eax
addl %esi, %eax
addl %esi, (%eax)
ljmp *(%eax)
jmpbuf_32:
.long primary_start_long_mode
/* 0x0008 = HOST_GDT_RING0_CODE_SEL */
.word 0x0008
/*
* Offset from the beginning of the entry section.
* This is to make sure that cpu_primary_start_64 is linked to a known address
* so that efi-stub knows where to pass control to hypervisor.
*/
.org 0x200
.code64
.global cpu_primary_start_64
cpu_primary_start_64:
/* save the MULTBOOT magic number & MBI */
lea boot_regs(%rip), %rax
movl %edi, (%rax)
movl %esi, 4(%rax)
/* Save boot context from 64bit mode */
call cpu_primary_save_64
primary_start_long_mode:
/* Initialize temporary stack pointer */
lea ld_bss_end(%rip), %rsp
/*0x1000 = PAGE_SIZE*/
add $0x1000,%rsp
/* 16 = CPU_STACK_ALIGN */
and $(~(16 - 1)),%rsp
/*
* Fix up the .rela sections
* Notes: this includes the fixup to IDT tables and temporary
* page tables
*/
call relocate
call 0f
0: pop %rsi
sub $0b, %rsi /* relocation delta */
/* Load temportary GDT pointer value */
lea cpu_primary64_gdt_ptr(%rip), %rbx
addq %rsi, 2(%rbx)
lgdt (%ebx)
/* Set the correct long jump address */
lea jmpbuf_64(%rip), %rax
lea after(%rip), %rbx
mov %rbx, (%rax)
rex.w ljmp *(%rax)
jmpbuf_64: .quad 0
/* 0x0008 = HOST_GDT_RING0_CODE_SEL */
.word 0x0008
after:
/* 0x10 = HOST_GDT_RING0_DATA_SEL*/
movl $0x10,%eax
mov %eax,%ss // Was 32bit POC Stack
mov %eax,%ds // Was 32bit POC Data
mov %eax,%es // Was 32bit POC Data
mov %eax,%fs // Was 32bit POC Data
mov %eax,%gs // Was 32bit POC CLS
/* continue with chipset level initialization */
call init_primary_pcpu
loop:
jmp loop
.align 4
.global boot_regs
boot_regs:
.long 0x00000000
.long 0x00000000
/* GDT table */
.align 4
cpu_primary32_gdt:
.quad 0x0000000000000000
.quad 0x00af9b000000ffff
.quad 0x00cf93000000ffff
cpu_primary32_gdt_end:
/* GDT pointer */
.align 2
cpu_primary32_gdt_ptr:
.short (cpu_primary32_gdt_end - cpu_primary32_gdt) - 1
.quad cpu_primary32_gdt
cpu_primary64_gdt_ptr:
.short (cpu_primary32_gdt_end - cpu_primary32_gdt) - 1
.quad cpu_primary32_gdt
/* PML4, PDPT, and PD tables initialized to map first 4 GBytes of memory */
/*0x1000 = PAGE_SIZE*/
.align 0x1000
.global cpu_boot32_page_tables_start
cpu_boot32_page_tables_start:
/* 0x3 = (PAGE_PRESENT | PAGE_RW) */
.quad cpu_primary32_pdpt_addr + 0x3
/*0x1000 = PAGE_SIZE*/
.align 0x1000
cpu_primary32_pdpt_addr:
address = 0
.rept 4
/* 0x3 = (PAGE_PRESENT | PAGE_RW) */
.quad cpu_primary32_pdt_addr + address + 0x3
/*0x1000 = PAGE_SIZE*/
address = address + 0x1000
.endr
/*0x1000 = PAGE_SIZE*/
.align 0x1000
cpu_primary32_pdt_addr:
address = 0
.rept 2048
/* 0x83 = (PAGE_PSE | PAGE_PRESENT | PAGE_RW) */
.quad address + 0x83
address = address + 0x200000
.endr
#ifdef CONFIG_MULTIBOOT2
.global efiloader_sig
efiloader_sig:
.asciz "EL64"
#endif