-
Notifications
You must be signed in to change notification settings - Fork 244
Plumb trap_reason from Sail traps into C callbacks #1415
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
base: master
Are you sure you want to change the base?
Conversation
model/sys/sys_control.sail
Outdated
| let t : sync_exception = struct { trap = e, | ||
| excinfo = xtval_exception_value(e, xtval), | ||
| ext = None() }; | ||
| function handle_exception_with_reason(xtval : xlenbits, e : ExceptionType, r : trap_reason) -> unit = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall but I personally dont think that we should rename the function. handle_exception() was good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the review!
I've kept handle_exception(xtval, e) as the main interface and added handle_exception_with_reason(xtval, e, r) as a helper. The old handle_exception now just forwards to the helper with Trap_reason_none(), so existing call sites can continue to use the original name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I missed that. But why not just modify handle_exception() function signature and pass the reason instead? Unless I’m missing something, that seems simpler. As of today, there are three call sites for handle_exception()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, that makes sense.
I've now removed handle_exception_with_reason and changed handle_exception to take (xtval, e, reason : trap_reason) directly. All existing call sites have been updated to pass an explicit trap_reason.
For the memory-related traps (Step_Fetch_Failure and Memory_Exception) I pass Access_not_in_physical_memory(), and for other traps I currently pass Trap_reason_none() until we add more specific reasons for them.
model/sys/sys_control.sail
Outdated
| let t : sync_exception = struct { trap = e, | ||
| excinfo = xtval_exception_value(e, xtval), | ||
| ext = None() }; | ||
| function handle_exception_with_reason(xtval : xlenbits, e : ExceptionType, r : trap_reason) -> unit = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I missed that. But why not just modify handle_exception() function signature and pass the reason instead? Unless I’m missing something, that seems simpler. As of today, there are three call sites for handle_exception()
| Step_Execute(Trap(priv, ctl, pc), _) => set_next_pc(exception_handler(priv, ctl, pc)), | ||
| Step_Execute(Memory_Exception(vaddr, e), _) => handle_exception(bits_of(vaddr), e), | ||
| Step_Execute(Memory_Exception(vaddr, e), _) => handle_exception_with_reason(bits_of(vaddr), e, Access_not_in_physical_memory()), | ||
| Step_Execute(Illegal_Instruction(), instbits) => handle_exception(zero_extend(instbits), E_Illegal_Instr()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you miss to pass a reason for Illegal_Instruction()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch — I missed that case initially.
Step_Execute(Illegal_Instruction(), instbits) now calls: handle_exception(zero_extend(instbits), E_Illegal_Instr(), Trap_reason_none())
model/sys/sys_control.sail
Outdated
| function handle_exception(xtval : xlenbits, e : ExceptionType) -> unit = { | ||
| handle_exception_with_reason(xtval, e, Trap_reason_none()) | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does Trap_reason_none() represent? A trap should always have a reason, or is this just a temporary placeholder and reasons are added gradually over time?
Unless I am missing something, I would remove it entirely (Trap_reason_none()) or at least wrap it in an option type like option(trap_reason).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trap_reason_none() is intended to mean "there is no additional debug information beyond the architectural ExceptionType".
Right now only a subset of traps populate a more specific trap_reason (for example the memory-related fetch/data access faults that motivated this change). For the rest, we still want to plumb a trap_reason value through the interfaces so that the C callbacks have a uniform signature, but we don't yet have anything more precise to attach, hence the Trap_reason_none() constructor.
I did consider modelling this as option(trap_reason) as you suggest, but using an explicit Trap_reason_none keeps the generated C interface a bit simpler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But isn't the whole point of this PR to get more clarity on why we trapped? If most call sites just pass Trap_reason_none(), we're not really getting that clarity, we just have extra work.
It’s just my opinion, but I think we should not use Trap_reason_none(). It would be better to create meaningful messages, for example in the make_landing_pad_exception case. You could simply add a new reason like ‘landing pad exception’ or something like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're absolutely right. I've reworked the patch along those lines:
- Introduced
Trap_reason_unclassifiedas an explicit "no extra information (yet)" value, and restricted its use to cases
where we genuinely don't have anything more to say (e.g. interrupts, some generic/legacy paths). - For fetch and generic memory exceptions, access faults (
E_Fetch_Access_Fault,E_Load_Access_Fault,E_SAMO_Access_Fault) now useAccess_not_in_physical_memory()instead of an uninformative default. make_landing_pad_exceptionnow uses a dedicatedLanding_pad_exception()trap reason, so landing-pad-related software checks are distinguishable on the C side.
model/core/types_ext.sail
Outdated
| union trap_reason = { | ||
| Trap_reason_unclassified : unit, | ||
| Access_not_in_physical_memory : unit, | ||
| Invalid_pte_reserved_bits_nonzero : (physaddrbits, xlenbits), | ||
| Landing_pad_exception : unit | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| union trap_reason = { | |
| Trap_reason_unclassified : unit, | |
| Access_not_in_physical_memory : unit, | |
| Invalid_pte_reserved_bits_nonzero : (physaddrbits, xlenbits), | |
| Landing_pad_exception : unit | |
| } | |
| union TrapReason = { | |
| Trap_Reason_Unclassified : unit, | |
| Access_Not_In_Physical_Memory : unit, | |
| Invalid_Pte_Reserved_Bits_Nonzero : (physaddrbits, xlenbits), | |
| Landing_Pad_Exception : unit | |
| } |
Seems like the majority of the code basis uses for union snake_case, with capital letters
model/core/types_ext.sail
Outdated
| union trap_reason = { | ||
| Trap_reason_unclassified : unit, | ||
| Access_not_in_physical_memory : unit, | ||
| Invalid_pte_reserved_bits_nonzero : (physaddrbits, xlenbits), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like Invalid_Pte_Reserved_bits_nonzero is not used?
model/postlude/step.sail
Outdated
| function memory_exception_reason(e : ExceptionType) -> trap_reason = | ||
| match e { | ||
| // These ExceptionType values indicate that the memory access | ||
| // failed at the level of the implemented physical memory | ||
| // (e.g. no backing memory / PMA issues / bus errors). | ||
| E_Fetch_Access_Fault() => Access_not_in_physical_memory(), | ||
| E_Load_Access_Fault() => Access_not_in_physical_memory(), | ||
| E_SAMO_Access_Fault() => Access_not_in_physical_memory(), | ||
|
|
||
| // All other exceptions (alignment, page faults, envcalls, | ||
| // software checks, extension-specific traps, etc.) are not yet | ||
| // classified more finely here. | ||
| _ => Trap_reason_unclassified() | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something that confuses me a bit, is that currently we do not provide precise reasons for why an exception was triggered. At the moment, this approach is quite similar to simply enabling and running with --trace-exception.
I think what needs to be done is, for example, in the case of a memory exception during pt_walk, we should return the underlying cause, as defined in PTW_Error. One idea would be to modify translateAddr() and implement something like:
Err(f, ext_ptw) => {
let exc = translationException(ac, f);
let reason = ptw_error_to_trap_reason(f);
Err((exc, reason), ext_ptw)
}
function ptw_error_to_trap_reason(f : PTW_Error) -> trap_reason = {
match f {
PTW_No_Access() => Access_not_in_physical_memory(),
// the one below is a new union entry
PTW_Reserved_Bits(addr, pte) => Invalid_pte_reserved_bits_nonzero(bits_of(addr), pte),
...
_ => Trap_reason_unclassified()
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’ve added a TrapReason union and wired PTW_Error to it as suggested, so PTW-related memory exceptions now carry both the ExceptionType and a more precise TrapReason.
2087de9 to
81341e2
Compare
|
a |
|
Was there a specific reason for closing this PR @ibvqeibob? |
I did not fully understand this part of the process before, so I closed the PR and reviewed it carefully. This revision passes the TrapReason (a more specific trap cause) all the way from the Sail model to the trap_callback in C.The implementation method is as follows: add TrapReason on the Sail side and map the corresponding causes in the exception generation paths (especially the memory exceptions related to page table walk/address translation); meanwhile, update the C callback interface signature to pass the reason parameter to the upper-layer callback. |
pmundkur
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has a lot of unrelated changes from other PRs. Could you rebase properly to isolate the changes for the traps/callbacks?
cbab7d3 to
9907ff6
Compare
Thanks for the heads-up! I’ve rebased the branch onto the latest upstream/master and squashed the work into a single commit. The PR now contains only the TrapReason plumbing from Sail to the C++ trap_callback, with no unrelated commits. |
Adds a
trap_reasonfield to Sail traps and exposes it to the C/C++ callback interface so that the emulator can log more detailed reasons for traps.Main changes:
trap_reasonunion and addreason : trap_reasontosync_exception.handle_exception_with_reason(xtval, e, reason)and keep the existinghandle_exceptionas a wrapper usingTrap_reason_none().trap_handler,exception_handlerandtrap_callbackto take and propagate atrap_reason.struct ztrap_reasonfromsail_riscv_model.hastypedef struct ztrap_reason trap_reasonand update the C/C++trap_callback(bool, fbits, trap_reason)interface.Step_Fetch_FailureandStep_Execute(Memory_Exception(...)), passAccess_not_in_physical_memory()as an initial example reason.log_callbacks::trap_callbackprints both the trap cause andreasonfor debugging.Testing:
sail_riscv_simsuccessfully.trap: is_interrupt=0 cause=1andreason: access_not_in_physical_memoryin the log.