Disclaimer: I am not an expert on the Armv8-a architecture, I have just been writing some bare-metal code dealing with exceptions on a Cortex-A53 for the purpose of learning.
The document you are pointing to explains, although succinctly, that:
- There are one stack pointer per exception level, i.e. SP_EL0 for EL0, SP_EL1 for EL1, SP_EL2 for EL2 and SP_EL3 for EL3,
- When you execute code at a given exception level, the stack pointer that will be used for storing the exception context and by the exception handler to access the saved context will depend on the value of the SPSel system register at the time the exception occurred.
From the Arm documentation for the SPSel system register:
Bits [63:1] Reserved, RES0.
SP, bit [0] Stack pointer to use. Possible values of this bit are:
0b0 Use SP_EL0 at all Exception levels.
0b1 Use SP_ELx for Exception level ELx.
The reset behaviour of this field is: On a Warm reset, this field resets to 1.
Using the stack pointer dedicated to a given exception level helps isolating more the code executing at, say, EL3, from the less-privileged code executing at EL2..EL0, since different memory areas can be used for implementing the stack for each exception level.
If you are writing your own bare-metal code, the value to set in SPSel would ultimately be your choice: For example, when using a standard Arm-Trusted firmware (code running at EL3)/u-boot (code running at EL2) bundle on an Alwinner H6 Cortex-A53:
SPSel.s:
.global _start
.align 3
.text
_start:
mrs x0, SPSel
ret
Building:
/opt/arm/11/gcc-arm-11.2-2022.02-x86_64-aarch64-none-elf/bin/aarch64-none-elf-gcc -nostartfiles -nostdlib --freestanding -Wl,--section-start=.text=0x40080000 -o SPSel.elf SPSel.s
/opt/arm/11/gcc-arm-11.2-2022.02-x86_64-aarch64-none-elf/bin/aarch64-none-elf-objcopy -O srec SPSel.elf SPSel.srec
/opt/arm/11/gcc-arm-11.2-2022.02-x86_64-aarch64-none-elf/bin/aarch64-none-elf-objdump -D -j .text SPSel.elf > SPSel.lst
Executing:
=> loads
## Ready for S-Record download ...
## First Load Addr = 0x40080000
## Last Load Addr = 0x40080007
## Total Size = 0x00000008 = 8 Bytes
## Start Addr = 0x40080000
=> go 0x40080000
## Starting application at 0x40080000 ...
## Application terminated, rc = 0x0
=>
The value of SPSel
returned in x0
is 0, i.e. the SP
bit of SPSel
is 0, and SP_EL0
is therefore the stack-pointer register that will be used at all EL3..EL0 exception levels.