| #include "compartment.h" |
| #include <cheri.hh> |
| #include <debug.hh> |
| #include <priv/riscv.h> |
| #include <simulator.h> |
| |
| using DebugErrorHandler = ConditionalDebug< |
| #ifdef NDEBUG |
| false |
| #else |
| true |
| #endif |
| , |
| "Error handler">; |
| |
| /** |
| * A error handler that reports unexpected traps and calls the scheduler to |
| * request that the simulator exit with exit code 1 (FAILURE). |
| * |
| * This is useful because the default behaviour in the absence of an error |
| * handler is to unwind the trusted stack and exit the thread when the top of |
| * the stack is reached. If all threads exit then the scheduler will exit the |
| * simulation with SUCCESS, which might mask errors. |
| * |
| * We attempt to detect the trap that occurs when a thread returns from its |
| * entry point function and do not halt the simulation as this is considered |
| * normal. |
| * |
| * Unwinds caused by errors in called compartments are also detected and are not |
| * treated as errors -- instead we resume execution at the call point with a |
| * return value of -1 as though this handler is not present. |
| * |
| * On non-simulation platforms this error handler reverts to the default force |
| * unwind behaviour. |
| * |
| * If NDEBUG is not defined then errors and thread exits will be logged. |
| * |
| * To use this simply include this file in your compartment's c / cc file. |
| */ |
| extern "C" ErrorRecoveryBehaviour |
| compartment_error_handler(ErrorState *frame, size_t mcause, size_t mtval) |
| { |
| if (mcause == priv::MCAUSE_CHERI) |
| { |
| // An unexpected error -- log it and end the simulation with error. |
| // Note: handle CZR differently as `get_register_value` will return a |
| // nullptr which we cannot dereference. |
| |
| auto [exceptionCode, registerNumber] = |
| CHERI::extract_cheri_mtval(mtval); |
| |
| DebugErrorHandler::log( |
| "{} error at {} (return address: {}), with capability register " |
| "{}: {}", |
| exceptionCode, |
| frame->pcc, |
| frame->get_register_value<CHERI::RegisterNumber::CRA>(), |
| registerNumber, |
| registerNumber == CHERI::RegisterNumber::CZR |
| ? nullptr |
| : *frame->get_register_value(registerNumber)); |
| } |
| else |
| { |
| // other error (e.g. __builtin_trap causes ReservedInstruciton) |
| // log and end simulation with error. |
| DebugErrorHandler::log("Unhandled error {} at {}", mcause, frame->pcc); |
| } |
| |
| simulation_exit(1); |
| /* |
| * simulation_exit may fail (say, we're not on a simulator or there isn't |
| * enough stack space to invoke the function. In that case, just fall back |
| * to forcibly unwinding. |
| */ |
| return ErrorRecoveryBehaviour::ForceUnwind; |
| } |