Files
Limine/common/sys/smp_trampoline.asm_x86

272 lines
5.5 KiB
Plaintext

bits 16
section .rodata
global smp_trampoline_start
smp_trampoline_start:
cli
cld
mov ebx, cs
shl ebx, 4
o32 lidt [cs:(invalid_idt - smp_trampoline_start)]
o32 lgdt [cs:(passed_info.gdtr - smp_trampoline_start)]
lea eax, [ebx + (.mode32 - smp_trampoline_start)]
mov [cs:(.farjmp_off - smp_trampoline_start)], eax
mov eax, 0x00000011
mov cr0, eax
o32 jmp far [cs:(.farjmp - smp_trampoline_start)]
.farjmp:
.farjmp_off: dd 0
.farjmp_seg: dd 0x18
bits 32
.mode32:
mov ax, 0x20
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
xor eax, eax
lldt ax
xor eax, eax
mov cr4, eax
mov esi, ebx
mov eax, 1
xor ecx, ecx
cpuid
test edx, 1 << 16
jz .no_pat
mov ecx, 0x277
mov eax, 0x00070406
mov edx, 0x00000105
wrmsr
.no_pat:
mov ebx, esi
mov ecx, 0x1b
rdmsr
test eax, (1 << 10)
jz .write_apic_msr
; Check if target also has x2APIC
test dword [ebx + (passed_info.bsp_apic_addr_msr_lo - smp_trampoline_start)], (1 << 10)
jnz .write_apic_msr
; AP is x2APIC but target is xAPIC: go through disabled state
btr eax, 11
btr eax, 10
wrmsr
.write_apic_msr:
mov eax, [ebx + (passed_info.bsp_apic_addr_msr_lo - smp_trampoline_start)]
mov edx, [ebx + (passed_info.bsp_apic_addr_msr_hi - smp_trampoline_start)]
bts eax, 11
btr eax, 8
wrmsr
mov esp, [ebx + (passed_info.temp_stack - smp_trampoline_start)]
mov eax, cr4
bts eax, 5
mov cr4, eax
; Set EFER (LME + NX if available), all other bits cleared
mov ecx, 0xc0000080
xor edx, edx
mov eax, 1 << 8
test dword [ebx + (passed_info.target_mode - smp_trampoline_start)], (1 << 3)
jz .no_nx
or eax, 1 << 11
.no_nx:
wrmsr
test dword [ebx + (passed_info.target_mode - smp_trampoline_start)], (1 << 1)
jz .no5lv
mov eax, cr4
bts eax, 12
mov cr4, eax
.no5lv:
mov eax, dword [ebx + (passed_info.pagemap - smp_trampoline_start)]
mov cr3, eax
; Enable CR0.PG (and WP if requested)
mov eax, cr0
test dword [ebx + (passed_info.target_mode - smp_trampoline_start)], (1 << 4)
jz .no_wp
bts eax, 16
.no_wp:
bts eax, 31
mov cr0, eax
%ifdef IA32_TARGET
; Synchronise MTRRs with BSP
call [ebx + (passed_info.mtrr_restore - smp_trampoline_start)]
; Configure local APIC handoff state (if pointer is set)
mov eax, dword [ebx + (passed_info.lapic_setup - smp_trampoline_start)]
test eax, eax
jz .skip_lapic_setup32
call eax
.skip_lapic_setup32:
%endif
lea eax, [ebx + (.mode64 - smp_trampoline_start)]
push 0x28
push eax
retf
bits 64
.mode64:
mov ax, 0x30
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov ebx, ebx
mov rax, qword [rbx + (passed_info.hhdm - smp_trampoline_start)]
add qword [rbx + (passed_info.gdtr - smp_trampoline_start) + 2], rax
lgdt [rbx + (passed_info.gdtr - smp_trampoline_start)]
; Clear TSS busy bit and load TR
mov rcx, [rbx + (passed_info.gdtr - smp_trampoline_start) + 2]
mov byte [rcx + 0x3d], 0x89
mov ecx, 0x38
ltr cx
lea rax, [rax + rbx + (parking64 - smp_trampoline_start)]
jmp rax
bits 64
parking64:
mov ebx, ebx
%ifdef X86_64_TARGET
; Synchronise MTRRs with BSP
call [rbx + (passed_info.mtrr_restore - smp_trampoline_start)]
; Configure local APIC handoff state (if pointer is set)
mov rax, [rbx + (passed_info.lapic_setup - smp_trampoline_start)]
test rax, rax
jz .skip_lapic_setup64
call rax
.skip_lapic_setup64:
%endif
mov edi, dword [rbx + (passed_info.smp_info_struct - smp_trampoline_start)]
add rdi, qword [rbx + (passed_info.hhdm - smp_trampoline_start)]
mov eax, 1
xchg dword [rbx + (passed_info.booted_flag - smp_trampoline_start)], eax
; Check for MONITOR/MWAIT support
mov eax, 1
xor ecx, ecx
cpuid
test ecx, (1 << 3)
jnz .monitor_spin
.loop:
mov rax, qword [rdi + 16]
test rax, rax
jnz .out
pause
jmp .loop
.monitor_spin:
mov rax, qword [rdi + 16]
test rax, rax
jnz .out
lea rax, [rdi + 16]
xor ecx, ecx
xor edx, edx
monitor
mov rax, qword [rdi + 16]
test rax, rax
jnz .out
xor eax, eax
xor ecx, ecx
mwait
jmp .monitor_spin
.out:
; Clear TLB
mov rbx, cr3
mov cr3, rbx
; Switch to new stack (HHDM address, safe after lower half unmap)
mov rsp, qword [rdi + 8]
; Push fake return address
push 0
mov rsi, rsp
; Prepare iretq frame
push 0x30
push rsi
push 0x2
push 0x28
push rax
; Zero out all GPRs (except rdi = mp_info pointer)
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx
xor esi, esi
xor ebp, ebp
xor r8d, r8d
xor r9d, r9d
xor r10d, r10d
xor r11d, r11d
xor r12d, r12d
xor r13d, r13d
xor r14d, r14d
xor r15d, r15d
iretq
invalid_idt:
times 2 dq 0
align 16
passed_info:
.booted_flag db 0
.target_mode db 0
.pagemap dd 0
.smp_info_struct dd 0
.gdtr:
dw 0
dq 0
.hhdm:
dq 0
.bsp_apic_addr_msr_lo:
dd 0
.bsp_apic_addr_msr_hi:
dd 0
.mtrr_restore:
dq 0
.temp_stack:
dq 0
.lapic_setup:
dq 0
smp_trampoline_end:
global smp_trampoline_size
smp_trampoline_size dq smp_trampoline_end - smp_trampoline_start
section .note.GNU-stack noalloc noexec nowrite progbits