-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
rust-tests: Some RISC-V emulation tests #1988
base: dev
Are you sure you want to change the base?
rust-tests: Some RISC-V emulation tests #1988
Conversation
There were previously no RISC-V emulation tests at all. Now there is a happy-path test of successfully executing a single instruction, along with two different ways of hooking an invalid instruction (using either general interrupt hook or invalid instruction hook) and also using general interrupt hook to handle an ecall instruction.
I've also now added some tests for handling of invalid memory accesses, since I found those weren't working as I expected in my program either. Again, I'm not sure if I'm just making incorrect assumptions about how it's intended to work. If these tests seem like they are valid then I'd be happy to try to investigate more deeply in the main Unicorn code, but I'd like to first confirm that I understand correctly what the intended behavior is. Thanks again! 😀 |
62d7dc2
to
761b0c9
Compare
Looking again with fresh eyes today I realized I didn't include enough instructions from my real program in the code loaded for the memory hook tests, and so what I had pushed yesterday was not actually exercising a memory access error at all -- it was only preparing the address to load from. I've pushed a complete version now which better matches the behavior I'm seeing in my real program: |
After reading the relevant code more deeply I've learned that:
I'm going to push another commit in a moment that includes the adjustments I described above, because I expect that'll make it easier to understand what I'm talking about. My goal here is still mainly to understand what the expected behavior is so that I can build my program around it. My use-case would benefit from some way to tell Unicorn to handle all of these fault cases as CPU exceptions passed to a single interrupt hook so that my program can more closely emulate what the real OS would do in its exception handler, but I understand that what I'm doing is unusual so I'm okay with faking that using multiple hooks just as long as I can understand how all of these hooks are intended to work. 😀 |
A TLB miss in virtual TLB mode causes Err(EXCEPTION) to be returned immediately, without calling any hooks or giving the target-specific code a chance to emulate a page fault exception. Therefore this now tests in the default TLB mode that uses target-specific TLB building logic.
(Sorry for the flood of comments. I'm learning more about Unicorn/QEMU as I investigate and sharing in case it's useful to others in the future.) I have identified one significant misunderstanding that affects my real program: by default the emulated RISC-V CPU is running in M-Mode (the most privileged mode) which works outside of the scope of the MMU. That mode-based exception is part of the CPU TLB fill, so it gets skipped when using virtual TLB and so the TLB hook would need to check the privilege mode itself to properly emulate the fact that M-Mode is always identity mapped. Side-note about that misunderstanding: I had misunderstood that the CPU was running in U-Mode (the unprivileged user mode) because the unicorn/qemu/target/riscv/insn_trans/trans_privileged.inc.c Lines 21 to 28 in 87610ba
unicorn/qemu/target/riscv/cpu_helper.c Lines 859 to 872 in 87610ba
Unfortunately the Unicorn interrupt hooks are called in As far as I can tell Unicorn does not expose any way to directly read or write the current privilege mode. That makes sense because that information is intentionally not exposed directly in any register in the ISA to allow for software-only virtualization where code designed for supervisor mode is actually running in usermode and then the supervisor emulates instructions that would normally require supervisor mode. However, that makes it challenging (impossible?) to properly emulate various RISC-V behaviors that are defined to vary based on privilege mode, because there's no way for the caller to discover the current privilege mode. I've made a local modification to Unicorn to expose a pseudo-register called |
This is interesting. I will have a check too. |
I have been working today on a PR to expose a "pseudo-register" that can be used to read or set the privilege mode. That design mimics part of the RISC-V Debug specification which recommends that a debugger expose the privilege information from one of the debug CSRs in a "virtual register" called I have implemented it and written a unit test but I ran out of time today before I had updated all of the bindings to support the new constant. I'm hoping to finish it tomorrow and then I'll open a separate PR for that. I'm not sure yet how that will interact with this PR, but I have at least proven that I can handle invalid instruction and page fault exceptions using an interrupt hook in my real program, without the more specialized hook types, once I got the MMU working by changing the emulator to use U-mode. |
Again, apologize for being late for your PRs because I'm not a RISC-V expert.
Since you are doing full system emulation, you should not use VTLB mode.
That's expected.
This is by design because we want the simple 1:1 mapping, i.e. skipping MMU as you discovered. The short reason for this design is that Unicorn was not really designed as a full system emulator, though I tried to bring back full system emulation capabilities in Unicorn 2 and recent releases.
This makes sense. I'm happy to accept a PR for fixing this. If you can fix CI, I'm happy to see this PR getting into dev branch. We lack unit tests for the uncommon architectures. |
Thank you for the feedback! If I recall correctly I wrote this PR before I worked on some others, and at the time I wrote this I was still a little confused about exactly what privilege mode Unicorn was running in and thus what behavior I should expect. The other PRs I submitted came later in my learning when I had (hopefully) understood the system better. I will try to find some time to finish this and hopefully make it suitable to merge. I will also consider how it might be possible to fix the incorrect exception code for ecall from M-mode or S-mode, and if I find a good solution I'll open a separate pull request for that. Unfortunately my attention has moved elsewhere now so I can't promise to work on this very soon, but I'll try to find some time. Thanks again! |
Instead of "raising the correct mode", I assume a more proper and easy fix is to pass the correct exception code in unicorn hooks (in the reverse direction of qemu implementation details). |
Thanks for creating Unicorn! I've been using it as part of an emulator for the syscall ABI of a toy RISC-V operating system.
I started working on this because I was seeing some strange behavior in my own program (invalid invalid instruction hook not being called) and so wanted to see if I could reproduce it without all of my code in play.
Now there is a happy-path test of successfully executing a single instruction, along with two different ways of hooking an invalid instruction (using either general interrupt hook or invalid instruction hook) and also using general interrupt hook to handle an ecall instruction.
As currently written the
emulate_riscv64_invalid_insn_hook
test fails, because the hook doesn't seem to run at all and so theemu_start
function returnsErr(UC_EXCEPTION)
. I'm not sure whether that's my own bug or if something is not working correctly in the main Unicorn code. 🤔 I've opened this PR as a draft in case someone can identify a mistake I could fix to make the instruction hook test work, in which case I'd propose to add these tests just to help reduce the risk of regressions in the RISC-V platform support.