#![allow(dead_code)]
use crate::{arch::driver::sbi::ecall::ecall1, mm::VirtAddr};
use core::arch::asm;

/// `sbi_set_timer` extension ID
pub const SET_TIMER_EID: usize = 0x00;
/// `sbi_console_putchar` extension ID
pub const CONSOLE_PUTCHAR_EID: usize = 0x01;
/// `sbi_console_getchar` extension ID
pub const CONSOLE_GETCHAR_EID: usize = 0x02;
/// `sbi_clear_ipi` extension ID
pub const CLEAR_IPI_EID: usize = 0x03;
/// `sbi_send_ipi` extension ID
pub const SEND_IPI_EID: usize = 0x04;
/// `sbi_remote_fence_i` extension ID
pub const REMOTE_FENCE_I_EID: usize = 0x05;
/// `sbi_remote_sfence_vma` extension ID
pub const REMOTE_SFENCE_VMA_EID: usize = 0x06;
/// `sbi_remote_sfence_vma_asid` extension ID
pub const REMOTE_SFENCE_VMA_ASID_EID: usize = 0x07;
/// `sbi_shutdown` extension ID
pub const SHUTDOWN_EID: usize = 0x08;

/// 计划在未来的某个时间触发中断。
///
/// ## 参数
///
/// - `stime`：要触发中断的绝对时间，以滴答为单位。如果`stime`小于当前时间，则不会触发中断。
///
/// ## 详情
///
/// 要清除计时器中断而不预约另一个计时器事件，可以将时间设置为无限远（`u64::MAX`）或
/// mask `sie` CSR的`STIE` 位。此函数将清除待处理计时器中断位。
///
/// 注意：`time` 是一个绝对时间，不是从调用时刻开始的偏移量。这意味着如果您想要设置一个未来`n`和tick之后
/// 触发的时钟，您需要首先读取 `time` CSR，然后将滴答数添加到该值。关于如何确定每个滴答的时间，
/// 这是平台依赖的，而时钟频率应在 CPU 节点的 `timebase-frequency` 属性中表达，如果可用的话。
#[inline]
pub unsafe fn set_timer(stime: u64) {
    #[cfg(target_arch = "riscv64")]
    unsafe {
        ecall1(stime as usize, SET_TIMER_EID, 0).ok();
    }

    #[cfg(target_arch = "riscv32")]
    unsafe {
        asm!(
            "ecall",
            inout ("a0") stime as usize => _,
            inout ("a1") (stime >> 32) as usize => _,
            in("a7") SET_TIMER_EID,
        );
    }
}

/// 将字符写入调试控制台。如果仍有待处理的控制台输出，此调用将阻塞。如果不存在控制台，则不会执行任何操作。
#[inline]
pub unsafe fn console_putchar(c: u8) {
    unsafe {
        ecall1(c.into(), CONSOLE_PUTCHAR_EID, 0).ok();
    }
}

/// 尝试从调试控制台获取一个字符。
/// 如果没有任何字符等待阅读，或者没有调试控制台设备，则此函数将返回[`None`]。
#[inline]
pub unsafe fn console_getchar() -> Option<u8> {
    let mut ret: i8;

    unsafe {
        asm!(
            "ecall",
            lateout("a0") ret,
            in("a7") CONSOLE_GETCHAR_EID,
        );
    }

    match ret {
        -1 => None,
        _ => Some(ret as u8),
    }
}

/// 清除current核心的待处理中断（IPIs）。
#[inline]
#[deprecated = "S模式可以直接清除`sip.SSIP` CSR位，因此无需调用此函数。"]
pub unsafe fn clear_ipi() {
    unsafe {
        asm!(
            "ecall",
            in("a7") CLEAR_IPI_EID,
            lateout("a0") _,
        );
    }
}

/// 向所有由`hart_mask`位掩码指定的核心发送中断（IPI）。接收到的中断表示为监视器软件中断。
///
/// ## 参数
/// - `hart_mask`: 一个长度为`n_harts / size_of::<usize>()`的二进制位向量，向上取整到下一个`usize`。
#[inline]
pub unsafe fn send_ipi(hart_mask: &[usize]) {
    unsafe {
        asm!(
            "ecall",
            inlateout("a0") hart_mask.as_ptr() => _,
            in("a7") SEND_IPI_EID,
        );
    }
}

/// 对指定的心脏（hart）执行 `FENCE.I` 指令
///
/// ## 参数
/// - `hart_mask`: 一个长度为 `n_harts / size_of::<usize>()` 的位矢量，
/// 向上取整到下一个 `usize」。
#[inline]
pub unsafe fn remote_fence_i(hart_mask: &[usize]) {
    unsafe {
        asm!(
            "ecall",
            inlateout("a0") hart_mask.as_ptr() => _,
            in("a7") REMOTE_FENCE_I_EID,
        );
    }
}

/// 在指定的hart上执行`SFENCE.VMA`指令
/// 为指定的虚拟内存范围（由`start`和`size`指定）执行。
///
/// ## 参数
/// - `hart_mask`: 一个长度为`n_harts / size_of::<usize>()`的二进制向量，
/// 向上取整到下一个`usize`。
/// - `start`: 要执行`SFENCE.VMA`的起始虚拟地址。
/// - `size`: 要对`start`执行的`SFENCE.VMA`的字节大小。例如，要失效一个
/// 包含2个4-KiB页面的区域，您会为`size`传递`8192`。
///
/// 如果`start`和`size`都为`0`，或者如果`size`为[`usize::MAX`]，则将执行完整的
/// `SFENCE.VMA`，而不仅仅是一个或多个页面大小的`SFENCE.VMA`。
#[inline]
pub unsafe fn remote_sfence_vma(hart_mask: &[usize], start: VirtAddr, size: usize) {
    unsafe {
        asm!(
            "ecall",
            inlateout("a0") hart_mask.as_ptr() => _,
            in("a1") start.data(),
            in("a2") size,
            in("a7") REMOTE_SFENCE_VMA_EID,
        );
    }
}

/// 在指定的hart上执行SFENCE.VMA指令
///
/// 仅针对指定的地址空间ID（ASID）执行虚拟内存范围指定的
/// start和size的hart_mask位掩码。
///
/// ## 参数
/// - `hart_mask`: 一个长度为`n_harts / size_of::<usize>()`的二进制向量，
/// 向上取整到下一个`usize`。
/// - `start`: 要执行`SFENCE.VMA`的起始虚拟地址。
/// - `size`: 要对`start`执行的`SFENCE.VMA`的字节大小。例如，要失效一个
/// 包含2个4-KiB页面的区域，您会为`size`传递`8192`。
/// - `asid`: 要执行`SFENCE.VMA`的地址空间ID。
///
/// 如果start和size都为0，或者如果size为[usize::MAX]，则将执行全
/// 部SFENCE.VMA，而不是多个页面大小的SFENCE.VMA`。
#[inline]
pub unsafe fn remote_sfence_vma_asid(hart_mask: &[usize], start: usize, size: usize, asid: usize) {
    unsafe {
        asm!(
            "ecall",
            inlateout("a0") hart_mask.as_ptr() => _,
            in("a1") start,
            in("a2") size,
            in("a3") asid,
            in("a7") REMOTE_SFENCE_VMA_ASID_EID,
        );
    }
}

/// 将所有核心置于关闭状态，此时处理器的执行模式比当前监督模式具有更高的特权。此调用不会返回。
#[inline]
pub unsafe fn shutdown() -> ! {
    unsafe {
        asm!(
            "ecall",
            in("a7") SHUTDOWN_EID,
            options(noreturn)
        );
    }
}
