272 lines
5.5 KiB
Plaintext
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
|