diff --git a/AK/Platform.h b/AK/Platform.h index 699b4dffbe..8d951a2a46 100644 --- a/AK/Platform.h +++ b/AK/Platform.h @@ -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 diff --git a/Kernel/Arch/x86/x86_64/Boot/boot.S b/Kernel/Arch/x86/common/Boot/boot.S similarity index 57% rename from Kernel/Arch/x86/x86_64/Boot/boot.S rename to Kernel/Arch/x86/common/Boot/boot.S index d5cfe1cac1..b6cb3da55e 100644 --- a/Kernel/Arch/x86/x86_64/Boot/boot.S +++ b/Kernel/Arch/x86/common/Boot/boot.S @@ -1,3 +1,6 @@ +#define _BOOTLOADER +#include + .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, . diff --git a/Kernel/Arch/x86/i386/Boot/ap_setup.S b/Kernel/Arch/x86/i386/Boot/ap_setup.S new file mode 100644 index 0000000000..253df086cb --- /dev/null +++ b/Kernel/Arch/x86/i386/Boot/ap_setup.S @@ -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, . diff --git a/Kernel/Arch/x86/i386/Boot/boot.S b/Kernel/Arch/x86/i386/Boot/boot.S deleted file mode 100644 index 06215c6087..0000000000 --- a/Kernel/Arch/x86/i386/Boot/boot.S +++ /dev/null @@ -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, . diff --git a/Kernel/Arch/x86/x86_64/Boot/ap_setup.S b/Kernel/Arch/x86/x86_64/Boot/ap_setup.S new file mode 100644 index 0000000000..2e31c8406e --- /dev/null +++ b/Kernel/Arch/x86/x86_64/Boot/ap_setup.S @@ -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, . diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index b2807dc8e0..0a5926e33a 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -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