/*
 * Copyright (c) 2024, Sönke Holz <soenke.holz@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

//
// /!\ Read setjmp.h before modifying this file!
//

#define DID_SAVE_SIGNAL_MASK_SLOT (26 * 8)
#define SAVED_SIGNAL_MASK_SLOT    (26 * 8 + 4)

// https://pubs.opengroup.org/onlinepubs/9699919799/functions/setjmp.html
// int setjmp(jmp_buf env)
// int _setjmp(jmp_buf env)
.global _setjmp
.global setjmp
.type _setjmp, @function
.type setjmp, @function
_setjmp:
setjmp:
    li a1, 0                             // Set savemask argument to 0

// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigsetjmp.html
// int sigsetjmp(sigjmp_buf env, int savemask)
.global sigsetjmp
.type sigsetjmp, @function
sigsetjmp:
    sw a1, DID_SAVE_SIGNAL_MASK_SLOT(a0) // Store savemask into did_save_signal_mask
    sw zero, SAVED_SIGNAL_MASK_SLOT(a0)  // Clear saved_signal_mask
    beqz a1, .Lsaveregs

    addi sp, sp, -16                     // Prepare ABI-compliant call to sigprocmask
    sd a0, 0(sp)
    sd ra, 8(sp)

    addi a2, a0, SAVED_SIGNAL_MASK_SLOT  // Set argument oldset
    li a1, 0                             // Set argument set
    li a0, 0                             // Set argument how
    call sigprocmask@plt

    ld ra, 8(sp)
    ld a0, 0(sp)
    addi sp, sp, 16

.Lsaveregs:
    sd s0, 0*8(a0)                       // Save registers
    sd s1, 1*8(a0)
    sd s2, 2*8(a0)
    sd s3, 3*8(a0)
    sd s4, 4*8(a0)
    sd s5, 5*8(a0)
    sd s6, 6*8(a0)
    sd s7, 7*8(a0)
    sd s8, 8*8(a0)
    sd s9, 9*8(a0)
    sd s10, 10*8(a0)
    sd s11, 11*8(a0)

    fsd fs0, 12*8(a0)                    // Save floating-point registers
    fsd fs1, 13*8(a0)                    // NOTE: We only support ABI_FLEN=64 in LibELF/Validation.cpp,
    fsd fs2, 14*8(a0)                    //       so we only save the lower 64 bits of the fs* registers.
    fsd fs3, 15*8(a0)
    fsd fs4, 16*8(a0)
    fsd fs5, 17*8(a0)
    fsd fs6, 18*8(a0)
    fsd fs7, 19*8(a0)
    fsd fs8, 20*8(a0)
    fsd fs9, 21*8(a0)
    fsd fs10, 22*8(a0)
    fsd fs11, 23*8(a0)

    sd sp, 24*8(a0)
    sd ra, 25*8(a0)

    li a0, 0
    ret

// https://pubs.opengroup.org/onlinepubs/9699919799/functions/longjmp.html
// void longjmp(jmp_buf env, int val)
// void _longjmp(jmp_buf env, int val)
.global _longjmp
.global longjmp
.type _longjmp, @function
.type longjmp, @function
_longjmp:
longjmp:
    ld s0, 0*8(a0)                       // Restore registers
    ld s1, 1*8(a0)
    ld s2, 2*8(a0)
    ld s3, 3*8(a0)
    ld s4, 4*8(a0)
    ld s5, 5*8(a0)
    ld s6, 6*8(a0)
    ld s7, 7*8(a0)
    ld s8, 8*8(a0)
    ld s9, 9*8(a0)
    ld s10, 10*8(a0)
    ld s11, 11*8(a0)

    fld fs0, 12*8(a0)                    // Restore floating-point registers
    fld fs1, 13*8(a0)                    // NOTE: We only support ABI_FLEN=64 in LibELF/Validation.cpp,
    fld fs2, 14*8(a0)                    //       so we only restore the lower 64 bits of the fs* registers.
    fld fs3, 15*8(a0)
    fld fs4, 16*8(a0)
    fld fs5, 17*8(a0)
    fld fs6, 18*8(a0)
    fld fs7, 19*8(a0)
    fld fs8, 20*8(a0)
    fld fs9, 21*8(a0)
    fld fs10, 22*8(a0)
    fld fs11, 23*8(a0)

    ld sp, 24*8(a0)
    ld ra, 25*8(a0)

    mv a0, a1
    bnez a0, .Lnonzero
    li a0, 1
.Lnonzero:
    ret
