One answer that hasn't been covered is the need for separation of concerns due to the limitations of the C language.
Generally bootloaders are written in a mix of Assembly and C, withregards to the very early boot stage in Assemblystartup code.
This is done to setup certain things like:
- allocating the C stack
- reading the stack pointer into the register
- reading the program counter into the register
- declaring reset vectors
- loading the second stage (initramfs) into RAM.
This is a very rough approximation of the steps taken and I am describing the ARM boot process, it is different again for x86 and other architectures.
However, the principle reason remains the same: allocating the C stack must be done from assembly.