.section .text
.balign 4096
.global exception_entry_base
exception_entry_base:
    csrwr   $t0, KSAVE_T0
    csrwr   $t1, KSAVE_T1

    csrrd   $t1, LA_CSR_PRMD
    andi    $t1, $t1, 0x3
    move    $t0, $sp
    bnez    $t1, .Ltrap_from_user

    csrwr   $t0, KSAVE_KSP
    addi.d  $sp, $sp, -{trapframe_size}
    b       .Ltrap_stack_ready

.Ltrap_from_user:
    csrrd   $sp, KSAVE_KSP
    csrwr   $t0, KSAVE_KSP

.Ltrap_stack_ready:
    csrrd   $t0, KSAVE_T0
    csrrd   $t1, KSAVE_T1
    PUSH_GENERAL_REGS

    csrrd   $t0, KSAVE_KSP
    csrwr   $r0, KSAVE_KSP
    csrrd   $t1, LA_CSR_PRMD
    csrrd   $t2, LA_CSR_ERA
    STD     $t0, $sp, 3
    STD     $t1, $sp, 32    // prmd
    STD     $t2, $sp, 33    // era

    move    $a0, $sp

    andi    $t1, $t1, 0x3
    bnez    $t1, .Lexit_user

    la.pcrel $ra, .Ltrap_return
    b       loongarch64_trap_handler

.Lexit_user:
    LDD     $sp, $a0, 0
    STD     $r0, $a0, 0
    LDD     $ra, $sp, 0
    LDD     $tp, $sp, 1
    LDD     $fp, $sp, 2
    LDD     $r21,$sp, 3
    LDD     $s0, $sp, 4
    LDD     $s1, $sp, 5
    LDD     $s2, $sp, 6
    LDD     $s3, $sp, 7
    LDD     $s4, $sp, 8
    LDD     $s5, $sp, 9
    LDD     $s6, $sp, 10
    LDD     $s7, $sp, 11
    LDD     $s8, $sp, 12

    addi.d  $sp, $sp, 14 * 8
    ret

.global enter_user
enter_user:
    addi.d  $sp, $sp, -14 * 8
    STD     $ra, $sp, 0
    STD     $tp, $sp, 1
    STD     $fp, $sp, 2
    STD     $r21,$sp, 3
    STD     $s0, $sp, 4
    STD     $s1, $sp, 5
    STD     $s2, $sp, 6
    STD     $s3, $sp, 7
    STD     $s4, $sp, 8
    STD     $s5, $sp, 9
    STD     $s6, $sp, 10
    STD     $s7, $sp, 11
    STD     $s8, $sp, 12

    STD     $sp, $a0, 0
    move    $sp, $a0
    csrwr   $a0, KSAVE_KSP

.Ltrap_return:
    LDD     $t0, $sp, 32    // prmd
    LDD     $t1, $sp, 33    // era
    csrwr   $t0, LA_CSR_PRMD
    csrwr   $t1, LA_CSR_ERA

    POP_GENERAL_REGS
    LDD     $sp, $sp, 3

    ertn

.balign 4096
.global handle_tlb_refill
handle_tlb_refill:
    csrwr   $t0, LA_CSR_TLBRSAVE
    csrrd   $t0, LA_CSR_PGD

    lddir   $t0, $t0, 3
    beqz    $t0, .Ltlb_invalid

    lddir   $t0, $t0, 2
    beqz    $t0, .Ltlb_invalid

    lddir   $t0, $t0, 1
    beqz    $t0, .Ltlb_invalid

    ldpte   $t0, 0
    ldpte   $t0, 1

    b       .Ltlb_refill

.Ltlb_invalid:
    csrrd   $t0, LA_CSR_TLBREHI
    ori     $t0, $t0, 0xC   // 4 KiB
    csrwr   $t0, LA_CSR_TLBREHI

    rotri.d $t0, $t0, 61
    ori     $t0, $t0, 3     // NR NX
    rotri.d $t0, $t0, 3

    csrwr   $t0, LA_CSR_TLBRELO0
    csrrd   $t0, LA_CSR_TLBRELO0
    csrwr   $t0, LA_CSR_TLBRELO1

.Ltlb_refill:
    tlbfill
    csrrd   $t0, LA_CSR_TLBRSAVE
    ertn
