// vi: set ft=asm :

.set MSR_IA32_FS_BASE, 0xC0000100

.macro SAVE_TASK_STATE
    push %rbp
    push %r15
    push %r14
    push %r13
    push %r12
    push %rbx
.endm

.macro LOAD_TASK_STATE
    pop %rbx
    pop %r12
    pop %r13
    pop %r14
    pop %r15
    pop %rbp
.endm

.global __x86_64_task_enter_user
.global __x86_64_task_enter_kernel
.global __x86_64_task_enter_from_fork
.global __x86_64_enter_task
.global __x86_64_switch_task
.global __x86_64_switch_and_drop

.section .text

__x86_64_task_enter_from_fork:
    xorq %rax, %rax

    xorq %rcx, %rcx
    xorq %r11, %r11

    popq %rdi
    popq %rsi
    popq %rdx
    popq %r10
    popq %r8
    popq %r9

    swapgs

    iretq

__x86_64_task_enter_user:
    // User stack pointer
    popq %rcx
    // Argument
    popq %rdi
    // Entry address
    popq %rax
    // Flags
    popq %rsi

    // SS:RSP
    pushq $0x1B
    pushq %rcx

    // RFLAGS
    pushq %rsi

    // CS:RIP
    pushq $0x23
    pushq %rax

    swapgs

    iretq

__x86_64_task_enter_kernel:
    // Argument
    popq %rdi
    // Entry address
    popq %rax

    // Alignment word + fake return address to terminate "call chain"
    pushq $0

    // Enable IRQ in RFLAGS
    pushfq
    popq %rdx
    or $(1 << 9), %rdx

    mov %rsp, %rcx

    // SS:RSP
    pushq $0x10
    pushq %rcx

    // RFLAGS
    pushq %rdx

    // CS:RIP
    pushq $0x08
    pushq %rax

    iretq

// %rsi - from struct ptr, %rdi - to struct ptr
__x86_64_switch_task:
    // save state to source stack
    SAVE_TASK_STATE
    mov %rsp, 0(%rsi)

    // load destination stack
    mov 0(%rdi), %rsp
    LOAD_TASK_STATE
    ret

__x86_64_switch_and_drop:
    mov 0(%rdi), %rsp

    // Call thread drop before loading the state
    mov %rsi, %rdi
    call __arch_drop_thread

    LOAD_TASK_STATE
    ret

// %rdi - to struct ptr
__x86_64_enter_task:
    mov 0(%rdi), %rsp
    LOAD_TASK_STATE
    ret
