bits 32

section .text

global limine_spinup_32
limine_spinup_32:
    ; If available, set PAT as:
    ; PAT0 -> WB  (06)
    ; PAT1 -> WT  (04)
    ; PAT2 -> UC- (07)
    ; PAT3 -> UC  (00)
    ; PAT4 -> WP  (05)
    ; PAT5 -> WC  (01)
    mov eax, 1
    xor ecx, ecx
    cpuid
    test edx, 1 << 16
    jz .no_pat
    mov eax, 0x00070406
    mov edx, 0x00000105
    mov ecx, 0x277
    wrmsr
  .no_pat:

    ; Enable CR4.LA57
    cmp dword [esp+4], 0 ; level5pg
    je .no_la57
    mov eax, cr4
    bts eax, 12
    mov cr4, eax
  .no_la57:

    ; Enable CR0.WP
    mov eax, cr0
    bts eax, 16
    mov cr0, eax

    cld

    mov eax, [esp+8] ; pagemap_top_lv
    mov cr3, eax

    ; Enable CR4.PAE
    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
    cmp dword [esp+32], 0 ; nx_available
    je .no_nx
    or eax, 1 << 11
  .no_nx:
    wrmsr

    ; Enable CR0.PG
    mov eax, cr0
    bts eax, 31
    mov cr0, eax

    ; Go 64
    push 0x28
    call .p1
  .p1:
    add dword [esp], .mode64 - .p1
    retfd

bits 64
  .mode64:
    mov eax, 0x30
    mov ds, eax
    mov es, eax
    mov fs, eax
    mov gs, eax
    mov ss, eax

    ; Load 64-bit GDT
    mov eax, [rsp+28] ; local_gdt
    lgdt [rax]

    ; Jump to higher half
    mov rax, qword [rsp+36]
    add rsp, rax
    call .p2
  .p2:
    add qword [rsp], .hh - .p2
    add qword [rsp], rax
    retq
  .hh:

    cmp dword [rsp+44], 1
    jb .no_unmap_lower_half

    ; Unmap lower half entirely
    mov rsi, cr3
    lea rdi, [rsi + rax]
    mov rcx, 256
    xor rax, rax
    rep stosq
    mov cr3, rsi

  .no_unmap_lower_half:

    ; Push fake return address
    mov rsi, [rsp+20] ; stack
    sub rsi, 8
    mov qword [rsi], 0

    ; Prepare iretq frame
    mov rax, qword [rsp+12] ; entry_point
    push 0x30
    push rsi
    push 0x2
    push 0x28
    push rax

    ; Zero out all GPRs
    xor eax, eax
    xor ebx, ebx
    xor ecx, ecx
    xor edx, edx
    xor esi, esi
    xor edi, edi
    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

section .note.GNU-stack noalloc noexec nowrite progbits
