Kernel: Merge the x86 and x86_64 boot code into a single file

They're mostly the same apart from some x86_64-specific parts.
This commit is contained in:
Gunnar Beutner 2021-07-05 00:55:09 +02:00 committed by Andreas Kling
parent ce6658acc1
commit 040fe0054b
Notes: sideshowbarker 2024-07-18 10:24:56 +09:00
6 changed files with 414 additions and 600 deletions

View file

@ -74,18 +74,19 @@
# define PAGE_SIZE sysconf(_SC_PAGESIZE)
#endif
#ifndef _BOOTLOADER
ALWAYS_INLINE int count_trailing_zeroes_32(unsigned int val)
{
#if defined(__GNUC__) || defined(__clang__)
# if defined(__GNUC__) || defined(__clang__)
return __builtin_ctz(val);
#else
# else
for (u8 i = 0; i < 32; ++i) {
if ((val >> i) & 1) {
return i;
}
}
return 0;
#endif
# endif
}
ALWAYS_INLINE int count_trailing_zeroes_32_safe(unsigned int val)
@ -94,6 +95,7 @@ ALWAYS_INLINE int count_trailing_zeroes_32_safe(unsigned int val)
return 32;
return count_trailing_zeroes_32(val);
}
#endif
#ifdef AK_OS_BSD_GENERIC
# define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC

View file

@ -1,3 +1,6 @@
#define _BOOTLOADER
#include <AK/Platform.h>
.code32
.set KERNEL_VIRTUAL_BASE, 0xc0000000
@ -12,9 +15,11 @@ kernel_cmdline:
.section .page_tables, "aw", @nobits
.align 4096
#if ARCH(X86_64)
.global boot_pml4t
boot_pml4t:
.skip 4096
#endif
.global boot_pdpt
boot_pdpt:
.skip 4096
@ -47,6 +52,7 @@ boot_pd3_pt1023:
/*
construct the following (64-bit PML4T) page table layout:
(the PML4T part is not used for 32-bit x86)
pml4t:
@ -89,20 +95,25 @@ the 9 page tables each contain 512 pte's that map individual 4KB pages
*/
#if ARCH(X86_64)
gdt64:
.quad 0
gdt64code:
.quad (1<<43) | (1<<44) | (1<<47) | (1<<53) /* executable, code segment, present, 64-bit */
.global gdt64ptr
gdt64ptr:
.short . - gdt64 - 1
.quad gdt64
.global code64_sel
.set code64_sel, gdt64code - gdt64
#endif
start:
cli
cld
#if ARCH(X86_64)
/* test for long mode presence, save the most important registers from corruption */
pushl %eax
pushl %edx
@ -119,6 +130,7 @@ continue:
popl %ebx
popl %edx
popl %eax
#endif
/* We don't know where the bootloader might have put the command line.
* It might be at an inconvenient location that we're not about to map,
@ -133,6 +145,7 @@ continue:
movl $(kernel_cmdline - KERNEL_VIRTUAL_BASE), %edi
rep movsl
#if ARCH(X86_64)
/* clear pml4t */
movl $(boot_pml4t - KERNEL_VIRTUAL_BASE), %edi
movl $1024, %ecx
@ -144,6 +157,7 @@ continue:
movl $(boot_pdpt - KERNEL_VIRTUAL_BASE), 0(%edi)
/* R/W + Present */
orl $0x3, 0(%edi)
#endif
/* clear pdpt */
movl $(boot_pdpt - KERNEL_VIRTUAL_BASE), %edi
@ -153,8 +167,13 @@ continue:
/* set up pdpt[0] and pdpt[3] */
movl $(boot_pdpt - KERNEL_VIRTUAL_BASE), %edi
#if ARCH(X86_64)
movl $((boot_pd0 - KERNEL_VIRTUAL_BASE) + 3), 0(%edi)
movl $((boot_pd3 - KERNEL_VIRTUAL_BASE) + 3), 24(%edi)
#else
movl $((boot_pd0 - KERNEL_VIRTUAL_BASE) + 1), 0(%edi)
movl $((boot_pd3 - KERNEL_VIRTUAL_BASE) + 1), 24(%edi)
#endif
/* clear pd0 */
movl $(boot_pd0 - KERNEL_VIRTUAL_BASE), %edi
@ -232,8 +251,14 @@ continue:
orl $0x3, 4088(%edi)
movl $0, 4092(%edi)
#if ARCH(X86_64)
/* point CR3 to PML4T */
movl $(boot_pml4t - KERNEL_VIRTUAL_BASE), %eax
#else
/* point CR3 to PDPT */
movl $(boot_pdpt - KERNEL_VIRTUAL_BASE), %eax
#endif
movl %eax, %cr3
/* enable PAE + PSE */
@ -241,18 +266,19 @@ continue:
orl $0x60, %eax
movl %eax, %cr4
#if ARCH(X86_64)
1:
/* Enter Long-mode! ref(https://wiki.osdev.org/Setting_Up_Long_Mode)*/
mov $0xC0000080, %ecx /* Set the C-register to 0xC0000080, which is the EFER MSR.*/
rdmsr /* Read from the model-specific register.*/
or $(1 << 8), %eax /* Set the LM-bit which is the 9th bit (bit 8).*/
wrmsr /* Write to the model-specific register.*/
#endif
/* enable PG */
movl %cr0, %eax
orl $0x80000000, %eax
movl %eax, %cr0
/* Now we are in 32-bit compatibility mode, We still need to load a 64-bit GDT */
/* set up stack */
mov $stack_top, %esp
@ -279,6 +305,8 @@ continue:
addl $KERNEL_VIRTUAL_BASE, %ebx
movl %ebx, multiboot_info_ptr
#if ARCH(X86_64)
/* Now we are in 32-bit compatibility mode, We still need to load a 64-bit GDT */
lgdt gdt64ptr
ljmpl $code64_sel, $1f
@ -290,200 +318,17 @@ continue:
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
#endif
call init
#if ARCH(X86_64)
add $4, %rsp
#else
add $4, %esp
#endif
cli
loop:
hlt
jmp loop
.extern init_ap
.type init_ap, @function
/*
The apic_ap_start function will be loaded to P0x00008000 where the APIC
will boot the AP from in real mode. This code also contains space for
special variables that *must* remain here. When initializing the APIC,
the code here gets copied to P0x00008000, the variables in here get
populated and then the the boot of the APs will be triggered. Having
the variables here allows us to access them from real mode. Also, the
code here avoids the need for relocation entries.
Basically, the variables between apic_ap_start and end_apic_ap_start
*MUST* remain here and cannot be moved into a .bss or any other location.
*/
.global apic_ap_start
.type apic_ap_start, @function
apic_ap_start:
.code16
cli
jmp $0x800, $(1f - apic_ap_start) /* avoid relocation entries */
1:
mov %cs, %ax
mov %ax, %ds
xor %ax, %ax
mov %ax, %sp
/* load the first temporary gdt */
lgdt (ap_cpu_gdtr_initial - apic_ap_start)
/* enable PM */
movl %cr0, %eax
orl $1, %eax
movl %eax, %cr0
ljmpl $8, $(apic_ap_start32 - apic_ap_start + 0x8000)
apic_ap_start32:
.code32
mov $0x10, %ax
mov %ax, %ss
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
movl $0x8000, %ebp
/* generate a unique ap cpu id (0 means 1st ap, not bsp!) */
xorl %eax, %eax
incl %eax
lock; xaddl %eax, (ap_cpu_id - apic_ap_start)(%ebp) /* avoid relocation entries */
movl %eax, %esi
/* find our allocated stack based on the generated id */
movl (ap_cpu_init_stacks - apic_ap_start)(%ebp, %eax, 4), %esp
/* check if we support NX and enable it if we do */
movl $0x80000001, %eax
cpuid
testl $0x100000, %edx
// TODO: Uncomment this
//je (1f - apic_ap_start + 0x8000)
/* turn on IA32_EFER.NXE */
movl $0xc0000080, %ecx
rdmsr
orl $0x800, %eax
wrmsr
1:
/* load the bsp's cr3 value */
movl (ap_cpu_init_cr3 - apic_ap_start)(%ebp), %eax
movl %eax, %cr3
/* Enter Long-mode! ref(https://wiki.osdev.org/Setting_Up_Long_Mode)*/
mov $0xC0000080, %ecx /* Set the C-register to 0xC0000080, which is the EFER MSR.*/
rdmsr /* Read from the model-specific register.*/
or $(1 << 8), %eax /* Set the LM-bit which is the 9th bit (bit 8).*/
wrmsr /* Write to the model-specific register.*/
/* enable PAE + PSE */
movl %cr4, %eax
orl $0x60, %eax
movl %eax, %cr4
/* enable PG */
movl %cr0, %eax
orl $0x80000000, %eax
movl %eax, %cr0
/* load the temporary 64-bit gdt from boot that points above 3GB */
lgdt gdt64ptr
/* jump above 3GB into our identity mapped area now */
ljmpl $code64_sel, $(apic_ap_start64 - apic_ap_start + 0xc0008000)
.code64
apic_ap_start64:
mov $0, %ax
mov %ax, %ss
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
/* flush the TLB */
movq %cr3, %rax
movq %rax, %cr3
movl $0xc0008000, %ebp
/* now load the final gdt and idt from the identity mapped area */
movq (ap_cpu_gdtr - apic_ap_start)(%rbp), %rax
lgdt (%rax)
movq (ap_cpu_idtr - apic_ap_start)(%rbp), %rax
lidt (%rax)
/* set same cr0 and cr4 values as the BSP */
movq (ap_cpu_init_cr0 - apic_ap_start)(%rbp), %rax
movq %rax, %cr0
movq (ap_cpu_init_cr4 - apic_ap_start)(%rbp), %rax
movq %rax, %cr4
/* push the Processor pointer this CPU is going to use */
movq (ap_cpu_init_processor_info_array - apic_ap_start)(%ebp), %rax
movq $KERNEL_VIRTUAL_BASE, %r8
addq %r8, %rax
movq 0(%rax, %rsi, 4), %rax
push %rax
/* push the cpu id, 0 representing the bsp and call into c++ */
incq %rsi
push %rsi
xor %ebp, %ebp
cld
/* We are in identity mapped P0x8000 and the BSP will unload this code
once all APs are initialized, so call init_ap but return to our
infinite loop */
movabs $loop, %rax
pushq %rax
movabs $init_ap, %rax
jmp *(%rax)
.align 4
.global apic_ap_start_size
apic_ap_start_size:
.2byte end_apic_ap_start - apic_ap_start
.align 4
ap_cpu_id:
.4byte 0x0
ap_cpu_gdt:
/* null */
.8byte 0x0
/* code */
.4byte 0x0000FFFF
.4byte 0x00cf9a00
/* data */
.4byte 0x0000FFFF
.4byte 0x00cf9200
ap_cpu_gdt_end:
ap_cpu_gdtr_initial:
.2byte ap_cpu_gdt_end - ap_cpu_gdt - 1
.4byte (ap_cpu_gdt - apic_ap_start) + 0x8000
.global ap_cpu_gdtr
ap_cpu_gdtr:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_idtr
ap_cpu_idtr:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_init_cr0
ap_cpu_init_cr0:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_init_cr3
ap_cpu_init_cr3:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_init_cr4
ap_cpu_init_cr4:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_init_processor_info_array
ap_cpu_init_processor_info_array:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_init_stacks
ap_cpu_init_stacks:
/* array of allocated stack pointers */
/* NOTE: ap_cpu_init_stacks must be the last variable before
end_apic_ap_start! */
.set end_apic_ap_start, .

View file

@ -0,0 +1,179 @@
.set KERNEL_VIRTUAL_BASE, 0xc0000000
.extern init_ap
.type init_ap, @function
/*
The apic_ap_start function will be loaded to P0x00008000 where the APIC
will boot the AP from in real mode. This code also contains space for
special variables that *must* remain here. When initializing the APIC,
the code here gets copied to P0x00008000, the variables in here get
populated and then the the boot of the APs will be triggered. Having
the variables here allows us to access them from real mode. Also, the
code here avoids the need for relocation entries.
Basically, the variables between apic_ap_start and end_apic_ap_start
*MUST* remain here and cannot be moved into a .bss or any other location.
*/
.global apic_ap_start
.type apic_ap_start, @function
apic_ap_start:
.code16
cli
jmp $0x800, $(1f - apic_ap_start) /* avoid relocation entries */
1:
mov %cs, %ax
mov %ax, %ds
xor %ax, %ax
mov %ax, %sp
/* load the first temporary gdt */
lgdt (ap_cpu_gdtr_initial - apic_ap_start)
/* enable PM */
movl %cr0, %eax
orl $1, %eax
movl %eax, %cr0
ljmpl $8, $(apic_ap_start32 - apic_ap_start + 0x8000)
apic_ap_start32:
.code32
mov $0x10, %ax
mov %ax, %ss
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
movl $0x8000, %ebp
/* generate a unique ap cpu id (0 means 1st ap, not bsp!) */
xorl %eax, %eax
incl %eax
lock; xaddl %eax, (ap_cpu_id - apic_ap_start)(%ebp) /* avoid relocation entries */
movl %eax, %esi
/* find our allocated stack based on the generated id */
movl (ap_cpu_init_stacks - apic_ap_start)(%ebp, %eax, 4), %esp
/* check if we support NX and enable it if we do */
movl $0x80000001, %eax
cpuid
testl $0x100000, %edx
je (1f - apic_ap_start + 0x8000)
/* turn on IA32_EFER.NXE */
movl $0xc0000080, %ecx
rdmsr
orl $0x800, %eax
wrmsr
1:
/* load the bsp's cr3 value */
movl (ap_cpu_init_cr3 - apic_ap_start)(%ebp), %eax
movl %eax, %cr3
/* enable PAE + PSE */
movl %cr4, %eax
orl $0x60, %eax
movl %eax, %cr4
/* enable PG */
movl %cr0, %eax
orl $0x80000000, %eax
movl %eax, %cr0
/* load a second temporary gdt that points above 3GB */
lgdt (ap_cpu_gdtr_initial2 - apic_ap_start + 0xc0008000)
/* jump above 3GB into our identity mapped area now */
ljmp $8, $(apic_ap_start32_2 - apic_ap_start + 0xc0008000)
apic_ap_start32_2:
/* flush the TLB */
movl %cr3, %eax
movl %eax, %cr3
movl $0xc0008000, %ebp
/* now load the final gdt and idt from the identity mapped area */
movl (ap_cpu_gdtr - apic_ap_start)(%ebp), %eax
lgdt (%eax)
movl (ap_cpu_idtr - apic_ap_start)(%ebp), %eax
lidt (%eax)
/* set same cr0 and cr4 values as the BSP */
movl (ap_cpu_init_cr0 - apic_ap_start)(%ebp), %eax
movl %eax, %cr0
movl (ap_cpu_init_cr4 - apic_ap_start)(%ebp), %eax
movl %eax, %cr4
/* push the Processor pointer this CPU is going to use */
movl (ap_cpu_init_processor_info_array - apic_ap_start)(%ebp), %eax
addl $KERNEL_VIRTUAL_BASE, %eax
movl 0(%eax, %esi, 4), %eax
push %eax
/* push the cpu id, 0 representing the bsp and call into c++ */
incl %esi
push %esi
xor %ebp, %ebp
cld
/* We are in identity mapped P0x8000 and the BSP will unload this code
once all APs are initialized, so call init_ap but return to our
infinite loop */
push $loop
ljmp $8, $init_ap
loop:
hlt
jmp loop
.align 4
.global apic_ap_start_size
apic_ap_start_size:
.2byte end_apic_ap_start - apic_ap_start
.align 4
ap_cpu_id:
.4byte 0x0
ap_cpu_gdt:
/* null */
.8byte 0x0
/* code */
.4byte 0x0000FFFF
.4byte 0x00cf9a00
/* data */
.4byte 0x0000FFFF
.4byte 0x00cf9200
ap_cpu_gdt_end:
ap_cpu_gdtr_initial:
.2byte ap_cpu_gdt_end - ap_cpu_gdt - 1
.4byte (ap_cpu_gdt - apic_ap_start) + 0x8000
ap_cpu_gdtr_initial2:
.2byte ap_cpu_gdt_end - ap_cpu_gdt - 1
.4byte (ap_cpu_gdt - apic_ap_start) + 0xc0008000
.global ap_cpu_gdtr
ap_cpu_gdtr:
.4byte 0x0 /* will be set at runtime */
.global ap_cpu_idtr
ap_cpu_idtr:
.4byte 0x0 /* will be set at runtime */
.global ap_cpu_init_cr0
ap_cpu_init_cr0:
.4byte 0x0 /* will be set at runtime */
.global ap_cpu_init_cr3
ap_cpu_init_cr3:
.4byte 0x0 /* will be set at runtime */
.global ap_cpu_init_cr4
ap_cpu_init_cr4:
.4byte 0x0 /* will be set at runtime */
.global ap_cpu_init_processor_info_array
ap_cpu_init_processor_info_array:
.4byte 0x0 /* will be set at runtime */
.global ap_cpu_init_stacks
ap_cpu_init_stacks:
/* array of allocated stack pointers */
/* NOTE: ap_cpu_init_stacks must be the last variable before
end_apic_ap_start! */
.set end_apic_ap_start, .

View file

@ -1,407 +0,0 @@
.set KERNEL_VIRTUAL_BASE, 0xc0000000
.section .stack, "aw", @nobits
stack_bottom:
.skip 32768
stack_top:
.global kernel_cmdline
kernel_cmdline:
.skip 4096
.section .page_tables, "aw", @nobits
.align 4096
.global boot_pdpt
boot_pdpt:
.skip 4096
.global boot_pd0
boot_pd0:
.skip 4096
.global boot_pd3
boot_pd3:
.skip 4096
.global boot_pd0_pt0
boot_pd0_pt0:
.skip 4096 * 4
.global boot_pd3_pts
boot_pd3_pts:
.skip 4096 * 16
.global boot_pd3_pt1023
boot_pd3_pt1023:
.skip 4096
.section .boot_text, "ax"
.global start
.type start, @function
.extern init
.type init, @function
.extern multiboot_info_ptr
.type multiboot_info_ptr, @object
/*
construct the following (32-bit PAE) page table layout:
pdpt
0: boot_pd0 (0-1GB)
1: n/a (1-2GB)
2: n/a (2-3GB)
3: boot_pd3 (3-4GB)
boot_pd0 : 512 pde's
0: boot_pd0_pt0 (0-2MB) (id 512 4KB pages)
boot_pd3 : 512 pde's
0: boot_pd3_pts[0] (3072-3074MB) (pseudo 512 4KB pages)
1: boot_pd3_pts[1] (3074-3076MB) (pseudo 512 4KB pages)
2: boot_pd3_pts[2] (3076-3078MB) (pseudo 512 4KB pages)
3: boot_pd3_pts[3] (3078-3080MB) (pseudo 512 4KB pages)
4: boot_pd3_pts[4] (3080-3082MB) (pseudo 512 4KB pages)
5: boot_pd3_pts[5] (3082-3084MB) (pseudo 512 4KB pages)
6: boot_pd3_pts[6] (3084-3086MB) (pseudo 512 4KB pages)
7: boot_pd3_pts[7] (3086-3088MB) (pseudo 512 4KB pages)
8: boot_pd3_pts[8] (3088-3090MB) (pseudo 512 4KB pages)
9: boot_pd3_pts[9] (3090-3076MB) (pseudo 512 4KB pages)
10: boot_pd3_pts[10] (3092-3094MB) (pseudo 512 4KB pages)
11: boot_pd3_pts[11] (3094-3096MB) (pseudo 512 4KB pages)
12: boot_pd3_pts[12] (3096-3098MB) (pseudo 512 4KB pages)
13: boot_pd3_pts[13] (3098-3100MB) (pseudo 512 4KB pages)
14: boot_pd3_pts[14] (3100-3102MB) (pseudo 512 4KB pages)
15: boot_pd3_pts[15] (3102-3104MB) (pseudo 512 4KB pages)
16: boot_pd3_pt1023 (4094-4096MB) (for page table mappings)
the 9 page tables each contain 512 pte's that map individual 4KB pages
*/
start:
cli
cld
/* We don't know where the bootloader might have put the command line.
* It might be at an inconvenient location that we're not about to map,
* so let's just copy it to a convenient location while we have the whole
* memory space identity-mapped anyway. :^)
*/
movl %ebx, %esi
addl $16, %esi
movl (%esi), %esi
movl $1024, %ecx
movl $(kernel_cmdline - KERNEL_VIRTUAL_BASE), %edi
rep movsl
/* clear pdpt */
movl $(boot_pdpt - KERNEL_VIRTUAL_BASE), %edi
movl $1024, %ecx
xorl %eax, %eax
rep stosl
/* set up pdpt[0] and pdpt[3] */
movl $(boot_pdpt - KERNEL_VIRTUAL_BASE), %edi
movl $((boot_pd0 - KERNEL_VIRTUAL_BASE) + 1), 0(%edi)
movl $((boot_pd3 - KERNEL_VIRTUAL_BASE) + 1), 24(%edi)
/* clear pd0 */
movl $(boot_pd0 - KERNEL_VIRTUAL_BASE), %edi
movl $1024, %ecx
xorl %eax, %eax
rep stosl
/* clear pd3 */
movl $(boot_pd3 - KERNEL_VIRTUAL_BASE), %edi
movl $1024, %ecx
xorl %eax, %eax
rep stosl
/* clear pd0's pt's */
movl $(boot_pd0_pt0 - KERNEL_VIRTUAL_BASE), %edi
movl $(1024 * 4), %ecx
xorl %eax, %eax
rep stosl
/* clear pd3's pt's */
movl $(boot_pd3_pts - KERNEL_VIRTUAL_BASE), %edi
movl $(1024 * 17), %ecx
xorl %eax, %eax
rep stosl
/* add boot_pd0_pt0 to boot_pd0 */
movl $(boot_pd0 - KERNEL_VIRTUAL_BASE), %edi
movl $(boot_pd0_pt0 - KERNEL_VIRTUAL_BASE), %eax
movl %eax, 0(%edi)
/* R/W + Present */
orl $0x3, 0(%edi)
/* add boot_pd3_pts to boot_pd3 */
movl $16, %ecx
movl $(boot_pd3 - KERNEL_VIRTUAL_BASE), %edi
movl $(boot_pd3_pts - KERNEL_VIRTUAL_BASE), %eax
1:
movl %eax, 0(%edi)
/* R/W + Present */
orl $0x3, 0(%edi)
addl $8, %edi
addl $4096, %eax
loop 1b
/* identity map the 0 to 2MB range */
movl $512, %ecx
movl $(boot_pd0_pt0 - KERNEL_VIRTUAL_BASE), %edi
xorl %eax, %eax
1:
movl %eax, 0(%edi)
/* R/W + Present */
orl $0x3, 0(%edi)
addl $8, %edi
addl $4096, %eax
loop 1b
/* pseudo identity map the 3072-3102MB range */
movl $(512 * 16), %ecx
movl $(boot_pd3_pts - KERNEL_VIRTUAL_BASE), %edi
xorl %eax, %eax
1:
movl %eax, 0(%edi)
/* R/W + Present */
orl $0x3, 0(%edi)
addl $8, %edi
addl $4096, %eax
loop 1b
/* create an empty page table for the top 2MB at the 4GB mark */
movl $(boot_pd3 - KERNEL_VIRTUAL_BASE), %edi
movl $(boot_pd3_pt1023 - KERNEL_VIRTUAL_BASE), 4088(%edi)
orl $0x3, 4088(%edi)
movl $0, 4092(%edi)
/* point CR3 to PDPT */
movl $(boot_pdpt - KERNEL_VIRTUAL_BASE), %eax
movl %eax, %cr3
/* enable PAE + PSE */
movl %cr4, %eax
orl $0x60, %eax
movl %eax, %cr4
/* enable PG */
movl %cr0, %eax
orl $0x80000000, %eax
movl %eax, %cr0
/* set up stack */
mov $stack_top, %esp
and $-16, %esp
/* jmp to an address above the 3GB mark */
movl $1f,%eax
jmp *%eax
1:
movl %cr3, %eax
movl %eax, %cr3
/* unmap the 0-1MB range, which isn't used after jmp-ing up here */
movl $256, %ecx
movl $(boot_pd0_pt0 - KERNEL_VIRTUAL_BASE), %edi
xorl %eax, %eax
1:
movl %eax, 0(%edi)
addl $8, %edi
loop 1b
/* jump into C++ land */
addl $KERNEL_VIRTUAL_BASE, %ebx
movl %ebx, multiboot_info_ptr
call init
add $4, %esp
cli
loop:
hlt
jmp loop
.extern init_ap
.type init_ap, @function
/*
The apic_ap_start function will be loaded to P0x00008000 where the APIC
will boot the AP from in real mode. This code also contains space for
special variables that *must* remain here. When initializing the APIC,
the code here gets copied to P0x00008000, the variables in here get
populated and then the the boot of the APs will be triggered. Having
the variables here allows us to access them from real mode. Also, the
code here avoids the need for relocation entries.
Basically, the variables between apic_ap_start and end_apic_ap_start
*MUST* remain here and cannot be moved into a .bss or any other location.
*/
.global apic_ap_start
.type apic_ap_start, @function
apic_ap_start:
.code16
cli
jmp $0x800, $(1f - apic_ap_start) /* avoid relocation entries */
1:
mov %cs, %ax
mov %ax, %ds
xor %ax, %ax
mov %ax, %sp
/* load the first temporary gdt */
lgdt (ap_cpu_gdtr_initial - apic_ap_start)
/* enable PM */
movl %cr0, %eax
orl $1, %eax
movl %eax, %cr0
ljmpl $8, $(apic_ap_start32 - apic_ap_start + 0x8000)
apic_ap_start32:
.code32
mov $0x10, %ax
mov %ax, %ss
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
movl $0x8000, %ebp
/* generate a unique ap cpu id (0 means 1st ap, not bsp!) */
xorl %eax, %eax
incl %eax
lock; xaddl %eax, (ap_cpu_id - apic_ap_start)(%ebp) /* avoid relocation entries */
movl %eax, %esi
/* find our allocated stack based on the generated id */
movl (ap_cpu_init_stacks - apic_ap_start)(%ebp, %eax, 4), %esp
/* check if we support NX and enable it if we do */
movl $0x80000001, %eax
cpuid
testl $0x100000, %edx
je (1f - apic_ap_start + 0x8000)
/* turn on IA32_EFER.NXE */
movl $0xc0000080, %ecx
rdmsr
orl $0x800, %eax
wrmsr
1:
/* load the bsp's cr3 value */
movl (ap_cpu_init_cr3 - apic_ap_start)(%ebp), %eax
movl %eax, %cr3
/* enable PAE + PSE */
movl %cr4, %eax
orl $0x60, %eax
movl %eax, %cr4
/* enable PG */
movl %cr0, %eax
orl $0x80000000, %eax
movl %eax, %cr0
/* load a second temporary gdt that points above 3GB */
lgdt (ap_cpu_gdtr_initial2 - apic_ap_start + 0xc0008000)
/* jump above 3GB into our identity mapped area now */
ljmp $8, $(apic_ap_start32_2 - apic_ap_start + 0xc0008000)
apic_ap_start32_2:
/* flush the TLB */
movl %cr3, %eax
movl %eax, %cr3
movl $0xc0008000, %ebp
/* now load the final gdt and idt from the identity mapped area */
movl (ap_cpu_gdtr - apic_ap_start)(%ebp), %eax
lgdt (%eax)
movl (ap_cpu_idtr - apic_ap_start)(%ebp), %eax
lidt (%eax)
/* set same cr0 and cr4 values as the BSP */
movl (ap_cpu_init_cr0 - apic_ap_start)(%ebp), %eax
movl %eax, %cr0
movl (ap_cpu_init_cr4 - apic_ap_start)(%ebp), %eax
movl %eax, %cr4
/* push the Processor pointer this CPU is going to use */
movl (ap_cpu_init_processor_info_array - apic_ap_start)(%ebp), %eax
addl $KERNEL_VIRTUAL_BASE, %eax
movl 0(%eax, %esi, 4), %eax
push %eax
/* push the cpu id, 0 representing the bsp and call into c++ */
incl %esi
push %esi
xor %ebp, %ebp
cld
/* We are in identity mapped P0x8000 and the BSP will unload this code
once all APs are initialized, so call init_ap but return to our
infinite loop */
push $loop
ljmp $8, $init_ap
.align 4
.global apic_ap_start_size
apic_ap_start_size:
.2byte end_apic_ap_start - apic_ap_start
.align 4
ap_cpu_id:
.4byte 0x0
ap_cpu_gdt:
/* null */
.8byte 0x0
/* code */
.4byte 0x0000FFFF
.4byte 0x00cf9a00
/* data */
.4byte 0x0000FFFF
.4byte 0x00cf9200
ap_cpu_gdt_end:
ap_cpu_gdtr_initial:
.2byte ap_cpu_gdt_end - ap_cpu_gdt - 1
.4byte (ap_cpu_gdt - apic_ap_start) + 0x8000
ap_cpu_gdtr_initial2:
.2byte ap_cpu_gdt_end - ap_cpu_gdt - 1
.4byte (ap_cpu_gdt - apic_ap_start) + 0xc0008000
.global ap_cpu_gdtr
ap_cpu_gdtr:
.4byte 0x0 /* will be set at runtime */
.global ap_cpu_idtr
ap_cpu_idtr:
.4byte 0x0 /* will be set at runtime */
.global ap_cpu_init_cr0
ap_cpu_init_cr0:
.4byte 0x0 /* will be set at runtime */
.global ap_cpu_init_cr3
ap_cpu_init_cr3:
.4byte 0x0 /* will be set at runtime */
.global ap_cpu_init_cr4
ap_cpu_init_cr4:
.4byte 0x0 /* will be set at runtime */
.global ap_cpu_init_processor_info_array
ap_cpu_init_processor_info_array:
.4byte 0x0 /* will be set at runtime */
.global ap_cpu_init_stacks
ap_cpu_init_stacks:
/* array of allocated stack pointers */
/* NOTE: ap_cpu_init_stacks must be the last variable before
end_apic_ap_start! */
.set end_apic_ap_start, .

View file

@ -0,0 +1,194 @@
.set KERNEL_VIRTUAL_BASE, 0xc0000000
.extern init_ap
.type init_ap, @function
/*
The apic_ap_start function will be loaded to P0x00008000 where the APIC
will boot the AP from in real mode. This code also contains space for
special variables that *must* remain here. When initializing the APIC,
the code here gets copied to P0x00008000, the variables in here get
populated and then the the boot of the APs will be triggered. Having
the variables here allows us to access them from real mode. Also, the
code here avoids the need for relocation entries.
Basically, the variables between apic_ap_start and end_apic_ap_start
*MUST* remain here and cannot be moved into a .bss or any other location.
*/
.global apic_ap_start
.type apic_ap_start, @function
apic_ap_start:
.code16
cli
jmp $0x800, $(1f - apic_ap_start) /* avoid relocation entries */
1:
mov %cs, %ax
mov %ax, %ds
xor %ax, %ax
mov %ax, %sp
/* load the first temporary gdt */
lgdt (ap_cpu_gdtr_initial - apic_ap_start)
/* enable PM */
movl %cr0, %eax
orl $1, %eax
movl %eax, %cr0
ljmpl $8, $(apic_ap_start32 - apic_ap_start + 0x8000)
apic_ap_start32:
.code32
mov $0x10, %ax
mov %ax, %ss
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
movl $0x8000, %ebp
/* generate a unique ap cpu id (0 means 1st ap, not bsp!) */
xorl %eax, %eax
incl %eax
lock; xaddl %eax, (ap_cpu_id - apic_ap_start)(%ebp) /* avoid relocation entries */
movl %eax, %esi
/* find our allocated stack based on the generated id */
movl (ap_cpu_init_stacks - apic_ap_start)(%ebp, %eax, 4), %esp
/* check if we support NX and enable it if we do */
movl $0x80000001, %eax
cpuid
testl $0x100000, %edx
// TODO: Uncomment this
//je (1f - apic_ap_start + 0x8000)
/* turn on IA32_EFER.NXE */
movl $0xc0000080, %ecx
rdmsr
orl $0x800, %eax
wrmsr
1:
/* load the bsp's cr3 value */
movl (ap_cpu_init_cr3 - apic_ap_start)(%ebp), %eax
movl %eax, %cr3
/* Enter Long-mode! ref(https://wiki.osdev.org/Setting_Up_Long_Mode)*/
mov $0xC0000080, %ecx /* Set the C-register to 0xC0000080, which is the EFER MSR.*/
rdmsr /* Read from the model-specific register.*/
or $(1 << 8), %eax /* Set the LM-bit which is the 9th bit (bit 8).*/
wrmsr /* Write to the model-specific register.*/
/* enable PAE + PSE */
movl %cr4, %eax
orl $0x60, %eax
movl %eax, %cr4
/* enable PG */
movl %cr0, %eax
orl $0x80000000, %eax
movl %eax, %cr0
/* load the temporary 64-bit gdt from boot that points above 3GB */
lgdt gdt64ptr
/* jump above 3GB into our identity mapped area now */
ljmpl $code64_sel, $(apic_ap_start64 - apic_ap_start + 0xc0008000)
.code64
apic_ap_start64:
mov $0, %ax
mov %ax, %ss
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
/* flush the TLB */
movq %cr3, %rax
movq %rax, %cr3
movl $0xc0008000, %ebp
/* now load the final gdt and idt from the identity mapped area */
movq (ap_cpu_gdtr - apic_ap_start)(%rbp), %rax
lgdt (%rax)
movq (ap_cpu_idtr - apic_ap_start)(%rbp), %rax
lidt (%rax)
/* set same cr0 and cr4 values as the BSP */
movq (ap_cpu_init_cr0 - apic_ap_start)(%rbp), %rax
movq %rax, %cr0
movq (ap_cpu_init_cr4 - apic_ap_start)(%rbp), %rax
movq %rax, %cr4
/* push the Processor pointer this CPU is going to use */
movq (ap_cpu_init_processor_info_array - apic_ap_start)(%ebp), %rax
movq $KERNEL_VIRTUAL_BASE, %r8
addq %r8, %rax
movq 0(%rax, %rsi, 4), %rax
push %rax
/* push the cpu id, 0 representing the bsp and call into c++ */
incq %rsi
push %rsi
xor %ebp, %ebp
cld
/* We are in identity mapped P0x8000 and the BSP will unload this code
once all APs are initialized, so call init_ap but return to our
infinite loop */
movabs $loop, %rax
pushq %rax
movabs $init_ap, %rax
jmp *(%rax)
loop:
hlt
jmp loop
.align 4
.global apic_ap_start_size
apic_ap_start_size:
.2byte end_apic_ap_start - apic_ap_start
.align 4
ap_cpu_id:
.4byte 0x0
ap_cpu_gdt:
/* null */
.8byte 0x0
/* code */
.4byte 0x0000FFFF
.4byte 0x00cf9a00
/* data */
.4byte 0x0000FFFF
.4byte 0x00cf9200
ap_cpu_gdt_end:
ap_cpu_gdtr_initial:
.2byte ap_cpu_gdt_end - ap_cpu_gdt - 1
.4byte (ap_cpu_gdt - apic_ap_start) + 0x8000
.global ap_cpu_gdtr
ap_cpu_gdtr:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_idtr
ap_cpu_idtr:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_init_cr0
ap_cpu_init_cr0:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_init_cr3
ap_cpu_init_cr3:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_init_cr4
ap_cpu_init_cr4:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_init_processor_info_array
ap_cpu_init_processor_info_array:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_init_stacks
ap_cpu_init_stacks:
/* array of allocated stack pointers */
/* NOTE: ap_cpu_init_stacks must be the last variable before
end_apic_ap_start! */
.set end_apic_ap_start, .

View file

@ -281,7 +281,7 @@ set(KERNEL_SOURCES
set(KERNEL_SOURCES
${KERNEL_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86/${KERNEL_ARCH}/ASM_wrapper.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86/${KERNEL_ARCH}/Boot/boot.S
${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86/${KERNEL_ARCH}/Boot/ap_setup.S
${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86/${KERNEL_ARCH}/InterruptEntry.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86/${KERNEL_ARCH}/Processor.cpp
)
@ -289,6 +289,7 @@ set(KERNEL_SOURCES
set(KERNEL_SOURCES
${KERNEL_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86/common/ASM_wrapper.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86/common/Boot/boot.S
${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86/common/Boot/multiboot.S
${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86/common/CPU.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Arch/x86/common/Interrupts.cpp