Bug Description
NutShell accepts and reads back mtvec = 0x200000008000005c, but a subsequent synchronous trap redirects execution to 0x000000008000005c.
The test writes a high-tagged direct-mode trap vector, verifies that csrr mtvec returns the same value, and executes ecall. The reference model uses the held XLEN-wide BASE as the trap target; because that address is not fetchable in the test platform, it then reports an instruction access fault at the high address. NutShell instead discards the upper bits and executes the low-address handler (li a0, 42).
This report does not assume that an implementation must support every possible mtvec value. mtvec is WARL, so NutShell may legalize unsupported address bits. The bug is the externally visible inconsistency between the value retained/read back by the CSR and the address actually used for trap redirection.
RISC-V Specification Requirement
The key requirement is internal consistency. If NutShell reads back mtvec = 0x200000008000005c, then that is the trap-vector BASE value software is allowed to observe. A later Direct-mode trap must use that same held BASE, unless the implementation first legalizes the unsupported high bits and exposes the legalized value on readback.
The Machine Privileged Specification defines mtvec as an MXLEN-bit WARL read/write register. An implementation may restrict the set of values it can hold. However:
- the held BASE value is the trap-vector base;
- in Direct mode, all traps set
pc to BASE;
- the low two address bits are supplied as zero to form the XLEN-bit address.
Reference: https://docs.riscv.org/reference/isa/v20260120/priv/machine.html#_machine_trap_vector_base_address_mtvec_register
A compliant implementation can therefore either legalize unsupported high bits on write (and expose the legalized value on readback), or preserve the held XLEN-wide BASE when generating the trap target. It must not report one held value while silently using a different low alias.
Steps to Reproduce
- Run the supplied
poc/program.elf under difftest.
- The program forms
low_trap_entry | 0x2000000000000000.
- It writes the result to
mtvec, reads it back, and executes ecall if readback is unchanged.
Essential source:
la t0, low_trap_entry
li t1, 0x2000000000000000
or t0, t0, t1
csrw mtvec, t0
csrr t2, mtvec
bne t2, t0, pass
ecall
Expected Result
Either of the following is internally consistent:
- NutShell legalizes/masks unsupported high BASE bits when writing
mtvec; csrr mtvec returns the legalized value, so the program detects that the original value was not retained; or
- NutShell retains the full value and uses
0x200000008000005c as the trap target, allowing the fetch path to execute there or raise the appropriate instruction access fault.
The supplied Spike trace follows the second behavior and reports an instruction access fault (mcause=1) at the high target.
Actual Result
NutShell reads back the full high value but jumps to the low alias:
csrw mtvec, t0 data 200000008000005c
csrr t2, mtvec data 200000008000005c
ecall
commit pc 000000008000005c ... li a0, 42
...
REF pc/mtvec/mepc/mtval = 200000008000005c
DUT pc = 000000008000005c
NS-2.zip
Bug Description
NutShell accepts and reads back
mtvec = 0x200000008000005c, but a subsequent synchronous trap redirects execution to0x000000008000005c.The test writes a high-tagged direct-mode trap vector, verifies that
csrr mtvecreturns the same value, and executesecall. The reference model uses the held XLEN-wide BASE as the trap target; because that address is not fetchable in the test platform, it then reports an instruction access fault at the high address. NutShell instead discards the upper bits and executes the low-address handler (li a0, 42).This report does not assume that an implementation must support every possible
mtvecvalue.mtvecis WARL, so NutShell may legalize unsupported address bits. The bug is the externally visible inconsistency between the value retained/read back by the CSR and the address actually used for trap redirection.RISC-V Specification Requirement
The key requirement is internal consistency. If NutShell reads back
mtvec = 0x200000008000005c, then that is the trap-vector BASE value software is allowed to observe. A later Direct-mode trap must use that same held BASE, unless the implementation first legalizes the unsupported high bits and exposes the legalized value on readback.The Machine Privileged Specification defines
mtvecas an MXLEN-bit WARL read/write register. An implementation may restrict the set of values it can hold. However:pcto BASE;Reference: https://docs.riscv.org/reference/isa/v20260120/priv/machine.html#_machine_trap_vector_base_address_mtvec_register
A compliant implementation can therefore either legalize unsupported high bits on write (and expose the legalized value on readback), or preserve the held XLEN-wide BASE when generating the trap target. It must not report one held value while silently using a different low alias.
Steps to Reproduce
poc/program.elfunder difftest.low_trap_entry | 0x2000000000000000.mtvec, reads it back, and executesecallif readback is unchanged.Essential source:
Expected Result
Either of the following is internally consistent:
mtvec;csrr mtvecreturns the legalized value, so the program detects that the original value was not retained; or0x200000008000005cas the trap target, allowing the fetch path to execute there or raise the appropriate instruction access fault.The supplied Spike trace follows the second behavior and reports an instruction access fault (
mcause=1) at the high target.Actual Result
NutShell reads back the full high value but jumps to the low alias:
NS-2.zip