.macro SAVE_REGS_FROM_EL1
    # Curretly `sp` points to the address of `Aarch64VCpu.host_stack_top`.
    sub     sp, sp, 34 * 8
    # Curretly `sp` points to the base address of `Aarch64VCpu.ctx`, which stores guest's `TrapFrame`.

    # Save general purpose registers into `Aarch64VCpu.ctx`
    stp     x0, x1, [sp]
    stp     x2, x3, [sp, 2 * 8]
    stp     x4, x5, [sp, 4 * 8]
    stp     x6, x7, [sp, 6 * 8]
    stp     x8, x9, [sp, 8 * 8]
    stp     x10, x11, [sp, 10 * 8]
    stp     x12, x13, [sp, 12 * 8]
    stp     x14, x15, [sp, 14 * 8]
    stp     x16, x17, [sp, 16 * 8]
    stp     x18, x19, [sp, 18 * 8]
    stp     x20, x21, [sp, 20 * 8]
    stp     x22, x23, [sp, 22 * 8]
    stp     x24, x25, [sp, 24 * 8]
    stp     x26, x27, [sp, 26 * 8]
    stp     x28, x29, [sp, 28 * 8]

    mrs     x9, sp_el0
    stp     x30, x9, [sp, 30 * 8]

    # Save `elr_el2` and `spsr_el2` into `Aarch64VCpu.ctx`
    mrs     x10, elr_el2
    mrs     x11, spsr_el2
    stp     x10, x11, [sp, 32 * 8]
.endm

.macro RESTORE_REGS_INTO_EL1
    ldp     x10, x11, [sp, 32 * 8]
    ldp     x30, x9, [sp, 30 * 8]
    msr     sp_el0, x9
    msr     elr_el2, x10
    msr     spsr_el2, x11

    ldr     x30,      [sp, 30 * 8]
    ldp     x28, x29, [sp, 28 * 8]
    ldp     x26, x27, [sp, 26 * 8]
    ldp     x24, x25, [sp, 24 * 8]
    ldp     x22, x23, [sp, 22 * 8]
    ldp     x20, x21, [sp, 20 * 8]
    ldp     x18, x19, [sp, 18 * 8]
    ldp     x16, x17, [sp, 16 * 8]
    ldp     x14, x15, [sp, 14 * 8]
    ldp     x12, x13, [sp, 12 * 8]
    ldp     x10, x11, [sp, 10 * 8]
    ldp     x8, x9, [sp, 8 * 8]
    ldp     x6, x7, [sp, 6 * 8]
    ldp     x4, x5, [sp, 4 * 8]
    ldp     x2, x3, [sp, 2 * 8]
    ldp     x0, x1, [sp]
    # Curretly `sp` points to the base address of `Aarch64VCpu.ctx`
    add     sp, sp, 34 * 8
     # Curretly `x0` points to the address of `Aarch64VCpu.host_stack_top`.
.endm


.macro INVALID_EXCP_EL2, kind, source
.p2align 7
    SAVE_REGS_FROM_EL1
    mov     x0, sp
    mov     x1, \kind
    mov     x2, \source
    bl      invalid_exception_el2
    b       .Lexception_return_el2
.endm

.macro HANDLE_CURRENT_IRQ
.p2align 7
    SAVE_REGS_FROM_EL1
    mov     x0, sp
    bl      current_el_irq_handler
    b       .Lexception_return_el2
.endm

.macro HANDLE_CURRENT_SYNC
.p2align 7
    SAVE_REGS_FROM_EL1
    mov     x0, sp
    bl      current_el_sync_handler
    b       .Lexception_return_el2
.endm

.macro HANDLE_LOWER_IRQ_VCPU
.p2align 7
    SAVE_REGS_FROM_EL1
    mov    x0, {exception_irq}
    bl     vmexit_trampoline
    # b .Lexception_return_el2 is called by `vmexit_trampoline`
.endm

.macro HANDLE_LOWER_SYNC_VCPU
.p2align 7
    SAVE_REGS_FROM_EL1
    mov    x0, {exception_sync}
    bl     vmexit_trampoline
    # b .Lexception_return_el2 is called by `vmexit_trampoline`
.endm


.section .text
.p2align 11
.global exception_vector_base_vcpu
exception_vector_base_vcpu:
    // current EL, with SP_EL0
    INVALID_EXCP_EL2 0 0
    INVALID_EXCP_EL2 1 0
    INVALID_EXCP_EL2 2 0
    INVALID_EXCP_EL2 3 0

    // current EL, with SP_ELx
    HANDLE_CURRENT_SYNC
    HANDLE_CURRENT_IRQ
    INVALID_EXCP_EL2 2 1
    INVALID_EXCP_EL2 3 1

    // lower EL, aarch64
    HANDLE_LOWER_SYNC_VCPU
    HANDLE_LOWER_IRQ_VCPU
    INVALID_EXCP_EL2 2 2
    INVALID_EXCP_EL2 3 2

    // lower EL, aarch32
    INVALID_EXCP_EL2 0 3
    INVALID_EXCP_EL2 1 3
    INVALID_EXCP_EL2 2 3
    INVALID_EXCP_EL2 3 3

.global context_vm_entry
context_vm_entry:
    # Curretly `x0` points to the address of `Aarch64VCpu.host_stack_top`.
    mov     sp, x0
    sub     sp, sp, 34 * 8
    # Curretly `sp` points to the base address of `Aarch64VCpu.ctx`, which stores guest's `TrapFrame`.
.Lexception_return_el2:
    RESTORE_REGS_INTO_EL1
    eret
