aboutsummaryrefslogtreecommitdiffstats
path: root/roms/opensbi/lib/sbi/sbi_emulate_csr.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/opensbi/lib/sbi/sbi_emulate_csr.c')
-rw-r--r--roms/opensbi/lib/sbi/sbi_emulate_csr.c188
1 files changed, 188 insertions, 0 deletions
diff --git a/roms/opensbi/lib/sbi/sbi_emulate_csr.c b/roms/opensbi/lib/sbi/sbi_emulate_csr.c
new file mode 100644
index 000000000..bee7761c4
--- /dev/null
+++ b/roms/opensbi/lib/sbi/sbi_emulate_csr.c
@@ -0,0 +1,188 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel <anup.patel@wdc.com>
+ */
+
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_encoding.h>
+#include <sbi/sbi_bitops.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_emulate_csr.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_timer.h>
+#include <sbi/sbi_trap.h>
+
+static bool hpm_allowed(int hpm_num, ulong prev_mode, bool virt)
+{
+ ulong cen = -1UL;
+
+ if (prev_mode <= PRV_S) {
+ cen &= csr_read(CSR_MCOUNTEREN);
+ if (virt)
+ cen &= csr_read(CSR_HCOUNTEREN);
+ }
+ if (prev_mode == PRV_U)
+ cen &= csr_read(CSR_SCOUNTEREN);
+
+ return ((cen >> hpm_num) & 1) ? TRUE : FALSE;
+}
+
+int sbi_emulate_csr_read(int csr_num, struct sbi_trap_regs *regs,
+ ulong *csr_val)
+{
+ int ret = 0;
+ struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
+ ulong prev_mode = (regs->mstatus & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT;
+#if __riscv_xlen == 32
+ bool virt = (regs->mstatusH & MSTATUSH_MPV) ? TRUE : FALSE;
+#else
+ bool virt = (regs->mstatus & MSTATUS_MPV) ? TRUE : FALSE;
+#endif
+
+ switch (csr_num) {
+ case CSR_HTIMEDELTA:
+ if (prev_mode == PRV_S && !virt)
+ *csr_val = sbi_timer_get_delta();
+ else
+ ret = SBI_ENOTSUPP;
+ break;
+ case CSR_CYCLE:
+ if (!hpm_allowed(csr_num - CSR_CYCLE, prev_mode, virt))
+ return SBI_ENOTSUPP;
+ *csr_val = csr_read(CSR_MCYCLE);
+ break;
+ case CSR_TIME:
+ /*
+ * We emulate TIME CSR for both Host (HS/U-mode) and
+ * Guest (VS/VU-mode).
+ *
+ * Faster TIME CSR reads are critical for good performance
+ * in S-mode software so we don't check CSR permissions.
+ */
+ *csr_val = (virt) ? sbi_timer_virt_value():
+ sbi_timer_value();
+ break;
+ case CSR_INSTRET:
+ if (!hpm_allowed(csr_num - CSR_CYCLE, prev_mode, virt))
+ return SBI_ENOTSUPP;
+ *csr_val = csr_read(CSR_MINSTRET);
+ break;
+
+#if __riscv_xlen == 32
+ case CSR_HTIMEDELTAH:
+ if (prev_mode == PRV_S && !virt)
+ *csr_val = sbi_timer_get_delta() >> 32;
+ else
+ ret = SBI_ENOTSUPP;
+ break;
+ case CSR_CYCLEH:
+ if (!hpm_allowed(csr_num - CSR_CYCLEH, prev_mode, virt))
+ return SBI_ENOTSUPP;
+ *csr_val = csr_read(CSR_MCYCLEH);
+ break;
+ case CSR_TIMEH:
+ /* Refer comments on TIME CSR above. */
+ *csr_val = (virt) ? sbi_timer_virt_value() >> 32:
+ sbi_timer_value() >> 32;
+ break;
+ case CSR_INSTRETH:
+ if (!hpm_allowed(csr_num - CSR_CYCLEH, prev_mode, virt))
+ return SBI_ENOTSUPP;
+ *csr_val = csr_read(CSR_MINSTRETH);
+ break;
+#endif
+
+#define switchcase_hpm(__uref, __mref, __csr) \
+ case __csr: \
+ if ((sbi_hart_mhpm_count(scratch) + 3) <= (__csr - __uref))\
+ return SBI_ENOTSUPP; \
+ if (!hpm_allowed(__csr - __uref, prev_mode, virt)) \
+ return SBI_ENOTSUPP; \
+ *csr_val = csr_read(__mref + __csr - __uref); \
+ break;
+#define switchcase_hpm_2(__uref, __mref, __csr) \
+ switchcase_hpm(__uref, __mref, __csr + 0) \
+ switchcase_hpm(__uref, __mref, __csr + 1)
+#define switchcase_hpm_4(__uref, __mref, __csr) \
+ switchcase_hpm_2(__uref, __mref, __csr + 0) \
+ switchcase_hpm_2(__uref, __mref, __csr + 2)
+#define switchcase_hpm_8(__uref, __mref, __csr) \
+ switchcase_hpm_4(__uref, __mref, __csr + 0) \
+ switchcase_hpm_4(__uref, __mref, __csr + 4)
+#define switchcase_hpm_16(__uref, __mref, __csr) \
+ switchcase_hpm_8(__uref, __mref, __csr + 0) \
+ switchcase_hpm_8(__uref, __mref, __csr + 8)
+
+ switchcase_hpm(CSR_CYCLE, CSR_MCYCLE, CSR_HPMCOUNTER3)
+ switchcase_hpm_4(CSR_CYCLE, CSR_MCYCLE, CSR_HPMCOUNTER4)
+ switchcase_hpm_8(CSR_CYCLE, CSR_MCYCLE, CSR_HPMCOUNTER8)
+ switchcase_hpm_16(CSR_CYCLE, CSR_MCYCLE, CSR_HPMCOUNTER16)
+
+#if __riscv_xlen == 32
+ switchcase_hpm(CSR_CYCLEH, CSR_MCYCLEH, CSR_HPMCOUNTER3H)
+ switchcase_hpm_4(CSR_CYCLEH, CSR_MCYCLEH, CSR_HPMCOUNTER4H)
+ switchcase_hpm_8(CSR_CYCLEH, CSR_MCYCLEH, CSR_HPMCOUNTER8H)
+ switchcase_hpm_16(CSR_CYCLEH, CSR_MCYCLEH, CSR_HPMCOUNTER16H)
+#endif
+
+#undef switchcase_hpm_16
+#undef switchcase_hpm_8
+#undef switchcase_hpm_4
+#undef switchcase_hpm_2
+#undef switchcase_hpm
+
+ default:
+ ret = SBI_ENOTSUPP;
+ break;
+ };
+
+ if (ret)
+ sbi_dprintf("%s: hartid%d: invalid csr_num=0x%x\n",
+ __func__, current_hartid(), csr_num);
+
+ return ret;
+}
+
+int sbi_emulate_csr_write(int csr_num, struct sbi_trap_regs *regs,
+ ulong csr_val)
+{
+ int ret = 0;
+ ulong prev_mode = (regs->mstatus & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT;
+#if __riscv_xlen == 32
+ bool virt = (regs->mstatusH & MSTATUSH_MPV) ? TRUE : FALSE;
+#else
+ bool virt = (regs->mstatus & MSTATUS_MPV) ? TRUE : FALSE;
+#endif
+
+ switch (csr_num) {
+ case CSR_HTIMEDELTA:
+ if (prev_mode == PRV_S && !virt)
+ sbi_timer_set_delta(csr_val);
+ else
+ ret = SBI_ENOTSUPP;
+ break;
+#if __riscv_xlen == 32
+ case CSR_HTIMEDELTAH:
+ if (prev_mode == PRV_S && !virt)
+ sbi_timer_set_delta_upper(csr_val);
+ else
+ ret = SBI_ENOTSUPP;
+ break;
+#endif
+ default:
+ ret = SBI_ENOTSUPP;
+ break;
+ };
+
+ if (ret)
+ sbi_dprintf("%s: hartid%d: invalid csr_num=0x%x\n",
+ __func__, current_hartid(), csr_num);
+
+ return ret;
+}