If there is a synchronous exception (load or store access fault, misaligned address, illegal instruction, etc) or asynchronous exception (timer, software or external interrupt) when the processor is in U mode, the mode will be switched based on the delegation registers (medeleg/mideleg) for the specific trap cause and the processor will jump to the address held in mtvec (which can in some cases be a hardwired address). mstatus.mpp is set to the last operating mode e.g. U mode if that is the mode the processor was in when the trap occured to be used later when returning from the trap. This is how the processor transitions from U mode to M mode.
Once the M mode software (or S mode in the case the exception has been delegated), has handled the exception by inspecting mcause, mbadaddr, and mepc, the M mode software can then take some action and set mepc (for example to advance past an illegal instruction, or to context switch to the program counter address of another process), and finally call mret. mret switches to the last mode that was stored in mstatus.mpp during the initial trap and then jumps to the address in mepc. Interrupt flags are also updated. mret is how M mode software transitions from M mode back to U mode (or whichever mode caused the trap).
This is a simplified explanation. There are a few more details. e.g. if the trap is delegated to S mode using medeleg/mideleg, then the processor will jump to the trap handler in stvec instead of mtvec and switch to S mode, and S mode returns to the mode that caused the trap using sret instead of mret, and the previous mode is held in sstatus.spp instead of mstatus.mpp.
Note a trap can occur in the same mode. i.e. S mode software can cause a trap and if the trap is delegated to S mode e.g. page fault, then the S mode handler in stvec is called and sret will return back to S mode. This is a horizontal trap.
You’re best bet is to read the Privileged Specification section on MRET and SRET and the section of the mstatus flags MPP, SPP, MIE, SIE, MPIE and SIE…