mirror of
https://gitee.com/bianbu-linux/opensbi
synced 2025-04-24 23:07:08 -04:00
197 lines
5.2 KiB
C
197 lines
5.2 KiB
C
/*
|
|
* 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;
|
|
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
|
|
|
if (prev_mode <= PRV_S) {
|
|
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10) {
|
|
cen &= csr_read(CSR_MCOUNTEREN);
|
|
if (virt)
|
|
cen &= csr_read(CSR_HCOUNTEREN);
|
|
} else {
|
|
cen = 0;
|
|
}
|
|
}
|
|
if (prev_mode == PRV_U) {
|
|
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10)
|
|
cen &= csr_read(CSR_SCOUNTEREN);
|
|
else
|
|
cen = 0;
|
|
}
|
|
|
|
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;
|
|
}
|