If it is synchronous, i.e. a syscall, then you save the saved registers along with ra and sp. If it is asynchronous, i.e. an exception, then you save all registers except x0. In the linux kernel, you can see this in https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/riscv/kernel/entry.S?h=v5.7-rc5
where save_context is for an exception and switch_to is for a context switch. The linux kernel doesn’t use FP registers, so it doesn’t need to save them when it is entered. They are handled separately for a context switch.
Yes, you have to manually save and restore all registers, and handle all interrupts/exceptions details, including dispatching to the handlers.
The RISC-V architecture is not optimised for context switches, like, for example the Cortex-M architecture, which directly calls C/C++ handlers, and delegates the context switching to a single PendSV handler: