diff --git a/src/init/acpi.asm b/src/init/acpi.asm index 460fe71..301957d 100644 --- a/src/init/acpi.asm +++ b/src/init/acpi.asm @@ -231,7 +231,7 @@ APICapic: ; Entry Type 0 jnc readAPICstructures ; Read the next structure if CPU not usable inc word [p_cpu_detected] xchg eax, edx ; Restore the APIC ID back to EAX - stosb ; Store the 8-bit APIC ID + stosd ; Store the 8-bit APIC ID as a 32-bit value jmp readAPICstructures ; Read the next structure ; I/O APIC Structure - 5.2.12.3 @@ -285,6 +285,7 @@ APICinterruptsourceoverride: ; Entry Type 2 jmp readAPICstructures ; Read the next structure ; Processor Local x2APIC Structure - 5.2.12.12 +; TODO - Check if the same ID was found via APICapic - if so, ignore ;APICx2apic: ; Entry Type 9 ; xor eax, eax ; xor edx, edx diff --git a/src/init/cpu.asm b/src/init/cpu.asm index aa9ab82..75b4f00 100644 --- a/src/init/cpu.asm +++ b/src/init/cpu.asm @@ -135,15 +135,19 @@ avx512_supported: xsetbv ; Save XCR0 register avx512_not_supported: -; Enable and Configure Local APIC -; mov ecx, IA32_APIC_BASE -; rdmsr -; bts eax, 11 ; APIC Global Enable -; cmp byte [p_x2APIC], 1 -; jne skip_x2APIC_enable -; bts eax, 10 ; Enable x2APIC mode -;skip_x2APIC_enable: -; wrmsr +; Enable APIC + mov ecx, IA32_APIC_BASE + rdmsr + bts eax, 11 ; EN - xAPIC global enable/disable + cmp byte [p_x2APIC], 1 + jne skip_x2APIC_enable + bts eax, 10 ; EXTD - Enable x2APIC mode +skip_x2APIC_enable: + wrmsr + + mov r8d, eax ; Save MSR value as bit 8 is checked later + +; Configure APIC mov ecx, APIC_TPR mov eax, 0x00000020 call apic_write ; Disable softint delivery @@ -153,12 +157,15 @@ avx512_not_supported: mov ecx, APIC_LVT_PERF mov eax, 0x00010000 call apic_write ; Disable performance counter interrupts + cmp byte [p_x2APIC], 1 + je skip_APIC ; Next 2 registers are APIC only mov ecx, APIC_LDR xor eax, eax call apic_write ; Set Logical Destination Register mov ecx, APIC_DFR not eax ; Set EAX to 0xFFFFFFFF; Bits 31-28 set for Flat Mode call apic_write ; Set Destination Format Register +skip_APIC: mov ecx, APIC_LVT_LINT0 mov eax, 0x00008700 ; Bit 15 (1 = Level), Bits 10:8 for Ext call apic_write ; Enable normal external interrupts @@ -174,10 +181,17 @@ avx512_not_supported: lock inc word [p_cpu_activated] mov ecx, APIC_ID - call apic_read ; APIC ID is stored in bits 31:24 + call apic_read + cmp byte [p_x2APIC], 1 + je skip_shift shr eax, 24 ; AL now holds the CPU's APIC ID (0 - 255) - mov rdi, IM_ActivedCoreIDs ; The location where the activated cores set their record to 1 - add rdi, rax ; RDI points to InfoMap CPU area + APIC ID. ex 0x5E01 would be APIC ID 1 +skip_shift: + bt r8d, 8 ; Check for BSP bit + jnc skip_bsp + mov [p_BSP], eax +skip_bsp: + mov edi, IM_ActivedCoreIDs ; The location where the activated cores set their record to 1 + add edi, eax ; RDI points to InfoMap CPU area + APIC ID. ex 0x5E01 would be APIC ID 1 mov al, 1 stosb ; Store a 1 as the core is activated @@ -186,26 +200,58 @@ avx512_not_supported: ; ----------------------------------------------------------------------------- ; apic_read -- Read from a register in the APIC ; IN: ECX = Register to read -; OUT: EAX = Register value +; OUT: RAX = Register value ; All other registers preserved apic_read: + cmp byte [p_x2APIC], 1 + je apic_read_x2 + +apic_read_x: mov rax, [p_LocalAPICAddress] mov eax, [rax + rcx] ret + +apic_read_x2: + push rdx + push rcx + shr ecx, 4 ; Convert xAPIC register to x2APIC + add ecx, 0x800 ; Add MSR base offset + rdmsr ; Read to EDX:EAX + shl rdx, 32 ; Shift to upper 32 bits + or rax, rdx ; Combine into RAX + pop rcx + pop rdx + ret ; ----------------------------------------------------------------------------- ; ----------------------------------------------------------------------------- ; apic_write -- Write to a register in the APIC ; IN: ECX = Register to write -; EAX = Value to write +; RAX = Value to write ; OUT: All registers preserved apic_write: + cmp byte [p_x2APIC], 1 + je apic_write_x2 + +apic_write_x: push rcx add rcx, [p_LocalAPICAddress] mov [rcx], eax pop rcx ret + +apic_write_x2: + push rdx + push rcx + shr ecx, 4 ; Convert xAPIC register to x2APIC + add ecx, 0x800 ; Add MSR base offset + mov rdx, rax ; Copy RAX to RDX + shr rdx, 32 ; Shift upper 32 bits to lower 32 + wrmsr ; Write as EDX:EAX + pop rcx + pop rdx + ret ; ----------------------------------------------------------------------------- @@ -227,6 +273,7 @@ APIC_TMR equ 0x180 ; Trigger Mode Register (Starting Address) APIC_IRR equ 0x200 ; Interrupt Request Register (Starting Address) APIC_ESR equ 0x280 ; Error Status Register ; 0x290 - 0x2E0 are Reserved +APIC_ICR equ 0x300 ; Interrupt Command Register (64-bit - x2APIC only) APIC_ICRL equ 0x300 ; Interrupt Command Register (low 32 bits) APIC_ICRH equ 0x310 ; Interrupt Command Register (high 32 bits) APIC_LVT_TMR equ 0x320 ; LVT Timer Register @@ -253,6 +300,38 @@ IA32_MTRR_PHYSMASK1 equ 0x203 IA32_PAT equ 0x277 IA32_MTRR_DEF_TYPE equ 0x2FF +; x2APIC MSR List +x2APIC_BASE equ 0x800 +; 0x000 - 0x001 are Reserved +x2APIC_ID equ 0x002 ; ID Register +x2APIC_VER equ 0x003 ; Version Register +; 0x004 - 0x007 are Reserved +x2APIC_TPR equ 0x008 ; Task Priority Register - Bits 31:8 are Reserved. +; 0x009 is Reserved +x2APIC_PPR equ 0x00A ; Processor Priority Register +x2APIC_EOI equ 0x00B ; End Of Interrupt - Write 0 Only +; 0x00C is Reserved +x2APIC_LDR equ 0x00D ; Logical Destination Register - Read Only +; 0x00E is Reserved +x2APIC_SPURIOUS equ 0x00F ; Spurious Interrupt Vector Register +x2APIC_ISR equ 0x010 ; In-Service Register (Starting Address) - Read Only +x2APIC_TMR equ 0x018 ; Trigger Mode Register (Starting Address) - Read Only +x2APIC_IRR equ 0x020 ; Interrupt Request Register (Starting Address) - Read Only +x2APIC_ESR equ 0x028 ; Error Status Register +; 0x029 - 0x02E are Reserved +x2APIC_ICR equ 0x030 ; Interrupt Command Register +x2APIC_LVT_TMR equ 0x032 ; LVT Timer Register +x2APIC_LVT_TSR equ 0x033 ; LVT Thermal Sensor Register +x2APIC_LVT_PERF equ 0x034 ; LVT Performance Monitoring Counters Register +x2APIC_LVT_LINT0 equ 0x035 ; LVT LINT0 Register +x2APIC_LVT_LINT1 equ 0x036 ; LVT LINT1 Register +x2APIC_LVT_ERR equ 0x037 ; LVT Error Register +x2APIC_TMRINITCNT equ 0x038 ; Initial Count Register (for Timer) +x2APIC_TMRCURRCNT equ 0x039 ; Current Count Register (for Timer) - Read Only +; 0x03A - 0x03D are Reserved +x2APIC_TMRDIV equ 0x03E ; Divide Configuration Register (for Timer) +x2APIC_SELF_IPI equ 0x03F ; Self IPI - Write Only +; 0x040 - 0x3FF are Reserved ; ============================================================================= ; EOF diff --git a/src/init/smp.asm b/src/init/smp.asm index 843539b..1903817 100644 --- a/src/init/smp.asm +++ b/src/init/smp.asm @@ -11,36 +11,48 @@ init_smp: cmp byte [cfg_smpinit], 1 ; Check if SMP should be enabled jne noMP ; If not then skip SMP init - ; Start the AP's one by one - xor eax, eax - xor edx, edx - mov rsi, [p_LocalAPICAddress] - mov eax, [rsi+0x20] ; Add the offset for the APIC ID location - shr rax, 24 ; APIC ID is stored in bits 31:24 - mov dl, al ; Store BSP APIC ID in DL - - mov esi, IM_DetectedCoreIDs + mov edx, [p_BSP] ; Get the BSP APIC ID + mov esi, IM_DetectedCoreIDs ; List of 32-bit APIC IDs xor eax, eax xor ecx, ecx mov cx, [p_cpu_detected] smp_send_INIT: cmp cx, 0 je smp_send_INIT_done - lodsb + lodsd - cmp al, dl ; Is it the BSP? - je smp_send_INIT_skipcore + cmp eax, edx ; Is it the BSP? + je smp_send_INIT_skipcore ; If so, skip + cmp byte [p_x2APIC], 1 + je smp_send_INIT_x2APIC + +smp_send_INIT_APIC: ; Send 'INIT' IPI to APIC ID in AL - mov rdi, [p_LocalAPICAddress] + push rcx ; Save counter shl eax, 24 - mov dword [rdi+0x310], eax ; Interrupt Command Register (ICR); bits 63-32 + mov ecx, APIC_ICRH ; Interrupt Command Register (ICR); bits 63-32 + call apic_write mov eax, 0x00004500 - mov dword [rdi+0x300], eax ; Interrupt Command Register (ICR); bits 31-0 + mov ecx, APIC_ICRL ; Interrupt Command Register (ICR); bits 31-0 + call apic_write smp_send_INIT_verify: - mov eax, [rdi+0x300] ; Interrupt Command Register (ICR); bits 31-0 + call apic_read bt eax, 12 ; Verify that the command completed jc smp_send_INIT_verify + pop rcx ; Restore counter + jmp smp_send_INIT_APIC_done + +smp_send_INIT_x2APIC: + ; Send 'INIT' IPI to APIC ID in AL + push rcx ; Save counter + mov ecx, APIC_ICR ; Interrupt Command Register (ICR); bits 63-0 + shl rax, 32 + mov ax, 0x4500 + call apic_write + pop rcx ; Restore counter + +smp_send_INIT_APIC_done: smp_send_INIT_skipcore: dec cl @@ -58,21 +70,40 @@ smp_send_INIT_done: smp_send_SIPI: cmp cx, 0 je smp_send_SIPI_done - lodsb + lodsd - cmp al, dl ; Is it the BSP? + cmp eax, edx ; Is it the BSP? je smp_send_SIPI_skipcore + cmp byte [p_x2APIC], 1 + je smp_send_SIPI_x2APIC + +smp_send_SIPI_APIC: ; Send 'Startup' IPI to destination using vector 0x08 to specify entry-point is at the memory-address 0x00008000 - mov rdi, [p_LocalAPICAddress] + push rcx shl eax, 24 - mov dword [rdi+0x310], eax ; Interrupt Command Register (ICR); bits 63-32 + mov ecx, APIC_ICRH ; Interrupt Command Register (ICR); bits 63-32 + call apic_write mov eax, 0x00004608 ; Vector 0x08 - mov dword [rdi+0x300], eax ; Interrupt Command Register (ICR); bits 31-0 + mov ecx, APIC_ICRL ; Interrupt Command Register (ICR); bits 31-0 + call apic_write smp_send_SIPI_verify: - mov eax, [rdi+0x300] ; Interrupt Command Register (ICR); bits 31-0 + call apic_read bt eax, 12 ; Verify that the command completed jc smp_send_SIPI_verify + pop rcx + jmp smp_send_SIPI_APIC_done + +smp_send_SIPI_x2APIC: + ; Send 'Startup' IPI to destination using vector 0x08 to specify entry-point is at the memory-address 0x00008000 + push rcx + mov ecx, APIC_ICR ; Interrupt Command Register (ICR); bits 63-0 + shl rax, 32 + mov ax, 0x4608 ; Vector 0x08 + call apic_write + pop rcx + +smp_send_SIPI_APIC_done: smp_send_SIPI_skipcore: dec cl @@ -85,13 +116,6 @@ smp_send_SIPI_done: call timer_delay noMP: - ; Gather and store the APIC ID of the BSP - xor eax, eax - mov rsi, [p_LocalAPICAddress] - add rsi, 0x20 ; Add the offset for the APIC ID location - lodsd ; APIC ID is stored in bits 31:24 - shr rax, 24 ; AL now holds the CPU's APIC ID (0 - 255) - mov [p_BSP], eax ; Store the BSP APIC ID ; Calculate base speed of CPU cpuid diff --git a/src/init/smp_ap.asm b/src/init/smp_ap.asm index 3815c96..cc73ff9 100644 --- a/src/init/smp_ap.asm +++ b/src/init/smp_ap.asm @@ -119,11 +119,26 @@ startap64: mov fs, ax mov gs, ax - ; Reset the stack. Each CPU gets a 1024-byte unique stack location + ; Gather the CPU APIC ID without using the stack + cmp byte [p_x2APIC], 1 + je get_id_x2APIC +get_id_APIC: mov rsi, [p_LocalAPICAddress] ; We would call p_smp_get_id here but the stack is not ... add rsi, 0x20 ; ... yet defined. It is safer to find the value directly. lodsd ; Load a 32-bit value. We only want the high 8 bits shr rax, 24 ; Shift to the right and AL now holds the CPU's APIC ID + jmp get_id_done +get_id_x2APIC: + mov ecx, IA32_APIC_BASE + rdmsr + bts eax, 11 ; APIC Global Enable + bts eax, 10 ; Enable x2APIC mode + wrmsr + mov ecx, 0x802 + rdmsr ; APIC ID returned in EAX +get_id_done: + + ; Set the stack. Each CPU gets a 1024-byte unique stack location shl rax, 10 ; shift left 10 bits for a 1024byte stack add rax, 0x0000000000090000 ; stacks decrement when you "push", start at 1024 bytes in mov rsp, rax ; Pure64 leaves 0x50000-0x9FFFF free so we use that diff --git a/src/pure64.asm b/src/pure64.asm index 91d61ec..655bf0c 100644 --- a/src/pure64.asm +++ b/src/pure64.asm @@ -720,7 +720,7 @@ pde_end: ; Read APIC Address from MSR and enable it (if not done so already) mov ecx, IA32_APIC_BASE rdmsr ; Returns APIC in EDX:EAX - bts eax, 11 ; APIC Global Enable + bts eax, 11 ; EN - xAPIC global enable wrmsr and eax, 0xFFFFF000 ; Clear lower 12 bits shl rdx, 32 ; Shift lower 32 bits to upper 32 bits @@ -729,10 +729,10 @@ pde_end: ; Check for x2APIC support mov eax, 1 - cpuid ; x2APIC is supported if bit 21 is set + cpuid ; x2APIC is supported if ECX[21] is set shr ecx, 21 and cl, 1 - mov byte [p_x2APIC], cl + mov byte [p_x2APIC], cl ; Set flag for x2APIC %ifndef NOVIDEO ; Visual Debug (3/8) @@ -799,15 +799,6 @@ pde_end: call debug_msg %endif -; Reset the stack to the proper location (was set to 0x8000 previously) - mov rsi, [p_LocalAPICAddress] ; We would call p_smp_get_id here but the stack is not ... - add rsi, 0x20 ; ... yet defined. It is safer to find the value directly. - lodsd ; Load a 32-bit value. We only want the high 8 bits - shr rax, 24 ; Shift to the right and AL now holds the CPU's APIC ID - shl rax, 10 ; shift left 10 bits for a 1024byte stack - add rax, 0x0000000000050400 ; stacks decrement when you "push", start at 1024 bytes in - mov rsp, rax ; Pure64 leaves 0x50000-0x9FFFF free so we use that - ; Build the InfoMap xor edi, edi mov di, 0x5000