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

#pragma once

#include <sys/arch/riscv64/regs.h>

#include <Kernel/Arch/riscv64/CSR.h>
#include <Kernel/Security/ExecutionMode.h>

#include <AK/Platform.h>
#include <AK/StdLibExtras.h>

VALIDATE_IS_RISCV64()

namespace Kernel {

struct alignas(16) RegisterState {
    u64 x[31];

    RISCV64::CSR::SSTATUS sstatus;
    u64 sepc;
    RISCV64::CSR::SCAUSE scause;
    u64 stval;

    // x86_64 uses its additional RegisterState member "userspace_rsp" here, which is also invalid if no privilege mode change happened.
    // On RISC-V, we only have one sp member, and regardless of the previous privilege mode, we always use this member here.
    FlatPtr userspace_sp() const { return x[1]; }
    void set_userspace_sp(FlatPtr value) { x[1] = value; }

    FlatPtr ip() const { return sepc; }
    void set_ip(FlatPtr value) { sepc = value; }

    FlatPtr bp() const { return x[7]; }
    void set_bp(FlatPtr value) { x[7] = value; }

    ExecutionMode previous_mode() const
    {
        switch (sstatus.SPP) {
        case RISCV64::CSR::SSTATUS::PrivilegeMode::User:
            return ExecutionMode::User;
        case RISCV64::CSR::SSTATUS::PrivilegeMode::Supervisor:
            return ExecutionMode::Kernel;
        default:
            VERIFY_NOT_REACHED();
        }
    }

    void set_return_reg(FlatPtr value) { x[9] = value; }
    void capture_syscall_params(FlatPtr& function, FlatPtr& arg1, FlatPtr& arg2, FlatPtr& arg3, FlatPtr& arg4) const
    {
        function = x[16];
        arg1 = x[9];
        arg2 = x[10];
        arg3 = x[11];
        arg4 = x[12];
    }
};

#define REGISTER_STATE_SIZE (36 * 8)
static_assert(AssertSize<RegisterState, REGISTER_STATE_SIZE>());

inline void copy_kernel_registers_into_ptrace_registers(PtraceRegisters& ptrace_regs, RegisterState const& kernel_regs)
{
    for (auto i = 0; i < 31; i++)
        ptrace_regs.x[i] = kernel_regs.x[i];

    ptrace_regs.pc = kernel_regs.ip();
}

inline void copy_ptrace_registers_into_kernel_registers(RegisterState& kernel_regs, PtraceRegisters const& ptrace_regs)
{
    for (auto i = 0; i < 31; i++)
        kernel_regs.x[i] = ptrace_regs.x[i];

    kernel_regs.set_ip(ptrace_regs.pc);
}

struct DebugRegisterState {
};

}
