Files
opensbi/lib/sbi/sbi_emulate_csr.c
Samuel Holland d8608e615f lib: sbi_emulate_csr: Do not log illegal CSR accesses
Illegal CSR accesses from lower privilege modes are delegated to S-mode
and do not necessarily indicate a bug. Supervisor software may want to
emulate some CSRs, or may intentionally disable access to certain
existing CSRs, and thus will expect traps when those CSRs are accessed.

For example, Linux disables sstatus.VS by default in order to detect
when userspace first accesses vector register state; this includes the
CSRs defined by the V extesion. As a result, if the first vector
instruction in a process is a CSR access, OpenSBI will log the illegal
instruction exception, even though there is no unexpected or erroneous
behavior occurring.

Since the illegal instruction exception is delegated to S-mode, S-mode
software should be responsible for reporting the exception, not OpenSBI.

Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
2024-07-04 10:42:16 +05:30

189 lines
4.9 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_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_mask(scratch) & (1 << (__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;
}
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;
}
return ret;
}