/*
* Copyright (c) 2010, 2012, 2013 Richard Braun.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
/*
* Boot GDT segment selectors.
*/
#define BOOT_GDT_SEL_NULL 0
#define BOOT_GDT_SEL_CODE 8
#define BOOT_GDT_SEL_DATA 16
#define BOOT_GDT_SEL_CODE64 24
#define BOOT_GDT_SEL_TLS 24
/*
* Convert a physical address in the .boot section to its real address in
* the MP trampoline code.
*/
#define BOOT_MP_ADDR_PTOT(addr) (BOOT_MP_TRAMPOLINE_ADDR + (addr) \
- boot_mp_trampoline)
.section .boot.hdr, "awx"
.code32
/*
* Multiboot header.
*/
ASM_DATA(boot_header)
.long MULTIBOOT_OS_MAGIC
.long MULTIBOOT_OS_FLAGS
.long -(MULTIBOOT_OS_FLAGS + MULTIBOOT_OS_MAGIC)
ASM_END(boot_header)
/*
* Entry point.
*/
ASM_ENTRY(_start)
lgdt boot_gdtr
/* Keep %eax and %ebx */
movl $BOOT_GDT_SEL_DATA, %ecx
movl %ecx, %ds
movl %ecx, %es
movl %ecx, %ss
xorl %ecx, %ecx
movl %ecx, %fs
movl %ecx, %gs
ljmp $BOOT_GDT_SEL_CODE, $1f
1:
movl $(boot_stack + BOOT_STACK_SIZE), %esp
#ifdef __LP64__
call boot_check_long_mode
call boot_setup_tls
call boot_setup_long_mode
/*
* At this point, the processor runs in long mode, but still uses the
* compatibility mode code segment. Switch to 64-bit mode with a far return.
*/
pushl $BOOT_GDT_SEL_CODE64
pushl $1f
lret
1:
.code64
movl %ebx, %edi
movl %eax, %esi
#else /* __LP64__ */
call boot_build_tls_seg_desc
movl $BOOT_GDT_SEL_TLS, %ecx
movl %ecx, %gs
pushl %eax
pushl %ebx
#endif /* __LP64__ */
call boot_setup_paging
#ifdef __LP64__
movq %rax, %cr3
movq $(boot_stack + BOOT_STACK_SIZE), %rsp
#else /* __LP64__ */
movl %eax, %cr3
movl %cr0, %eax
orl $CPU_CR0_PG, %eax
movl %eax, %cr0
ljmp $BOOT_GDT_SEL_CODE, $1f
1:
movl $(boot_stack + BOOT_STACK_SIZE), %esp
#endif /* __LP64__ */
xorl %ebp, %ebp
call boot_main
/* Never reached */
nop
ASM_END(_start)
.code32
ASM_DATA(boot_gdtr)
.word boot_gdt_end - boot_gdt - 1
.long boot_gdt
ASM_END(boot_gdtr)
#ifdef __LP64__
/*
* The %eax and %ebx registers must be preserved.
*/
ASM_ENTRY(boot_check_long_mode)
pushl %eax
pushl %ebx
movl $0x80000000, %eax
cpuid
cmpl $0x80000000, %eax
jbe boot_no_long_mode
movl $0x80000001, %eax
cpuid
testl $CPU_FEATURE4_LM, %edx
jz boot_no_long_mode
popl %ebx
popl %eax
ret
ASM_END(boot_check_long_mode)
ASM_ENTRY(boot_no_long_mode)
movl $boot_panic_long_mode_msg, %esi
movl $BOOT_CGAMEM, %edi
movl $(BOOT_CGAMEM + (BOOT_CGACHARS * 2)), %edx
movw $(BOOT_CGACOLOR << 8), %ax
1:
movb (%esi), %al
testb %al, %al
jz 1f
movw %ax, (%edi)
incl %esi
add $2, %edi
cmpl %edx, %edi
jae 2f
jmp 1b
1:
cmpl %edx, %edi
jae 2f
movw $((BOOT_CGACOLOR << 8) + ' '), (%edi)
add $2, %edi
jmp 1b
2:
hlt
jmp 2b
ASM_END(boot_no_long_mode)
ASM_ENTRY(boot_setup_long_mode)
/* Set PML4[0] */
movl $boot_pdpt, %edx
orl $(PMAP_PTE_RW | PMAP_PTE_P), %edx
movl %edx, boot_pml4
/* Set PDPT[0] through PDPT[3] */
movl $boot_pdir, %edx
orl $(PMAP_PTE_RW | PMAP_PTE_P), %edx
movl $boot_pdpt, %edi
movl $4, %ecx
1:
movl %edx, (%edi)
addl $PAGE_SIZE, %edx
addl $8, %edi
loop 1b
/* Set PDIR[0] through PDIR[2047] */
movl $(PMAP_PTE_PS | PMAP_PTE_RW | PMAP_PTE_P), %edx
movl $boot_pdir, %edi
movl $2048, %ecx
1:
movl %edx, (%edi)
addl $(1 << PMAP_L1_SKIP), %edx
addl $8, %edi
loop 1b
/* Switch to long mode */
movl %eax, %edi
movl %cr4, %eax
orl $CPU_CR4_PAE, %eax
movl %eax, %cr4
movl $boot_pml4, %eax
movl %eax, %cr3
movl $CPU_MSR_EFER, %ecx
rdmsr
orl $CPU_EFER_LME, %eax
wrmsr
movl %cr0, %eax
orl $CPU_CR0_PG, %eax
movl %eax, %cr0
ljmp $BOOT_GDT_SEL_CODE, $1f
1:
movl %edi, %eax
ret
ASM_END(boot_setup_long_mode)
ASM_ENTRY(boot_setup_tls)
pushl %eax
movl $boot_tls_seg, %eax
movl $0, %edx
movl $CPU_MSR_GSBASE, %ecx
wrmsr
popl %eax
ret
ASM_END(boot_setup_tls)
#else /* __LP64__ */
/*
* Complete the temporary boot TLS segment descriptor and copy it into
* the boot GDT.
*
* The %eax and %ebx registers must be preserved.
*/
ASM_ENTRY(boot_build_tls_seg_desc)
pushl %eax
/* Load boot_tls_seg_desc address */
movl $boot_tls_seg_desc, %ecx
/* Add the base 15:0 bits to boot_tls_seg_desc.low */
movl $boot_tls_seg, %eax
shll $16, %eax
orl %eax, (%ecx)
/* Add the base 23:16 bits to boot_tls_seg_desc.high */
movl $boot_tls_seg, %eax
shrl $16, %eax
andl $0xff, %eax
orl %eax, 4(%ecx)
/* Add the base 31:24 bits to boot_tls_seg_desc.high */
movl $boot_tls_seg, %eax
andl $0xff000000, %eax
orl %eax, 4(%ecx)
/* Load boot_gdt[BOOT_GDT_SEL_TLS] address */
movl $boot_gdt + BOOT_GDT_SEL_TLS, %edx
/* Set boot_gdt[BOOT_GDT_SEL_TLS].low */
movl (%ecx), %eax
movl %eax, (%edx)
/* Set boot_gdt[BOOT_GDT_SEL_TLS].high */
movl 4(%ecx), %eax
movl %eax, 4(%edx)
popl %eax
ret
ASM_END(boot_build_tls_seg_desc)
#endif /* __LP64__ */
/*
* This is where an AP runs after leaving the trampoline code.
*/
ASM_ENTRY(boot_ap_start32)
/*
* Set up the GDT again, because the current one is from the trampoline code
* which isn't part of the identity mapping and won't be available once paging
* is enabled.
*/
lgdt boot_gdtr
movl $BOOT_GDT_SEL_DATA, %eax
movl %eax, %ds
movl %eax, %es
movl %eax, %ss
xorl %eax, %eax
movl %eax, %fs
movl $BOOT_GDT_SEL_TLS, %eax
movl %eax, %gs
ljmp $BOOT_GDT_SEL_CODE, $1f
1:
movl $(boot_ap_stack + BOOT_STACK_SIZE), %esp
#ifdef __LP64__
call boot_setup_tls
call boot_setup_long_mode
pushl $BOOT_GDT_SEL_CODE64
pushl $1f
lret
1:
.code64
#endif /* __LP64__ */
call pmap_ap_setup_paging
#ifdef __LP64__
movq %rax, %cr3
#else /* __LP64__ */
movl %eax, %cr3
movl %cr0, %eax
orl $CPU_CR0_PG, %eax
movl %eax, %cr0
ljmp $BOOT_GDT_SEL_CODE, $1f
1:
#endif /* __LP64__ */
call cpu_get_boot_stack
#ifdef __LP64__
movq %rax, %rsp
addq $BOOT_STACK_SIZE, %rsp
#else /* __LP64__ */
movl %eax, %esp
addl $BOOT_STACK_SIZE, %esp
#endif /* __LP64__ */
xorl %ebp, %ebp
call boot_ap_main
/* Never reached */
nop
ASM_END(boot_ap_start32)
.code32
/*
* This part, including the GDT, is the MP trampoline code run by APs
* on startup. It is copied at a fixed location in the first segment and
* must enable protected mode to jump back into the kernel.
*/
ASM_ENTRY(boot_mp_trampoline)
.code16
cli
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
lgdt BOOT_MP_ADDR_PTOT(boot_ap_gdtr)
movl %cr0, %eax
orl $CPU_CR0_PE, %eax
movl %eax, %cr0
ljmp $BOOT_GDT_SEL_CODE, $BOOT_MP_ADDR_PTOT(1f)
.align 4
1:
.code32
movl $BOOT_GDT_SEL_DATA, %eax
movl %eax, %ds
movl %eax, %es
movl %eax, %ss
xorl %eax, %eax
movl %eax, %fs
movl %eax, %gs
ljmp $BOOT_GDT_SEL_CODE, $boot_ap_start32
ASM_END(boot_mp_trampoline)
ASM_DATA(boot_ap_gdtr)
.word boot_gdt_end - boot_gdt - 1
.long BOOT_MP_ADDR_PTOT(boot_gdt)
ASM_END(boot_ap_gdtr)
ASM_DATA(boot_gdt)
.quad 0x0000000000000000 /* Null descriptor */
.quad 0x00cf9a000000ffff /* Code segment descriptor */
.quad 0x00cf92000000ffff /* Data segment descriptor */
#ifdef __LP64__
.quad 0x00209a0000000000 /* 64-bit code segment selector */
#else /* __LP64__ */
.quad 0x0 /* TLS segment descriptor, filled at boot time */
#endif /* __LP64__ */
ASM_END(boot_gdt)
boot_gdt_end:
ASM_DATA(boot_mp_trampoline_size)
.long . - boot_mp_trampoline
ASM_END(boot_mp_trampoline_size)