Suppose I have configured all exceptions and interrupts to be handled in machine-mode...
How do my machine-mode handlers know to put PRV_U or PRV_M, etc. into mstatus.MPP before executing mret so execution may continue in the privilege mode the trap was taken from?
Another way to ask the same question is how do the CSRs look different upon arrival in my machine-mode handlers when arriving from User/Supervisor/Hypervisor/Machine mode...?
For example...
uintptr_t
exception_handler(uint64_t cause)
{
const uint64_t hartid = read_csr(mhartid);
switch (cause) {
// Do stuff to "claim", "handle", and "clear" each exception "cause"...
// Maybe it's an ecall? Maybe a page-fault? Doesn't matter...
}
clear_csr(mstatus, MSTATUS_MPP);
set_csr(mstatus, (trek_uint64_t)(???? << 11)); // <-- what goes here!?!!
return read_csr(mepc) + 4; // There is an `mret` just after this return.
}