Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

riscv: Incorrect program counter when handling exception with interrupt hook #1990

Open
apparentlymart opened this issue Aug 28, 2024 · 0 comments

Comments

@apparentlymart
Copy link
Contributor

apparentlymart commented Aug 28, 2024

Thanks for building Unicorn! It's been very helpful in my project.

I've been trying to emulate the behavior of a toy operating system using Unicorn so that I can run its applications on other operating systems and architectures. I'm using interrupt hooks to react to synchronous exceptions such as system calls, invalid instruction exceptions, and page faults.

Unfortunately Unicorn's treatment of the program counter in interrupt hooks is slightly incompatible with the RISC-V specification: when handling an exception it increments the program counter by 4 while the ISA specification1 calls for the exception program counter register (mepc in M-Mode, sepc in S-Mode) to contain the address of the instruction that caused the exception.

#if defined(TARGET_RISCV)
CPURISCVState *env = &(RISCV_CPU(uc->cpu)->env);
env->pc += 4;
#endif

I see that this was added intentionally in response to #1477, but this makes it hard to treat exceptions the same way as real hardware would.

I think it's also a little flawed: this behavior is plausible for ecall because that instruction is always encoded in 4 bytes, but other exception types can be caused by 2-byte instructions from the "C" extension. For example, the C.LW instruction is 2 bytes long and can generate a page fault, which would then cause pc to be pointing to an incorrect address in the interrupt hook.

I understand that existing callers are probably depending on the current behavior successfully for ecall, and they might not care about handling other fault types or might not be using 2-byte instructions. Therefore I assume maintainers would prefer to keep the current behavior even though it produces a wrong result in some cases.

Therefore I'm not really sure what to suggest. For now I think I'm going to work around this by having my program immediately subtract 4 from the program counter on entry into my interrupt hook. That should work for now because Unicorn unconditionally adds 4 regardless of the exception type or instruction length, but I would be broken if a future Unicorn release would change to only add two to pc for a fault on a 2-byte instruction. 🤔

Is it reasonable for me to assume that the current behavior will never change in future? Although not ideal, I'd be satisfied with this just being documented that on RISC-V the interrupt hook is always called with the program counter set four bytes too high, as long as I can rely on that remaining true in future.

Thanks again!


1 The RISC-V Instruction Set Manual: Volume II: Privileged Architecture, version 20240411, section 3.1.14

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant