|  | #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); | 
|  | } | 
|  |  | 
|  | #ifdef SIMULATION | 
|  | /* | 
|  | * 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. | 
|  | */ | 
|  | (void)scheduler_simulation_exit(1); | 
|  | #endif | 
|  |  | 
|  | return ErrorRecoveryBehaviour::ForceUnwind; | 
|  | } |