Search code examples
armkernelarm64armv8

What is SP0 and SPn in AArch64?


If the exception is being taken at the same Exception level, the stack pointer to be used (SP0 or SPn)

In a document of AArch64 exception vector table, an entry is selected based off the factor above. I couldn't find any useful information regarding SP0 and SPn, so I'd like to ask why there are two stack pointers and what's the difference between them? A link is also appreciated!


Solution

  • 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 SPSelis 0, and SP_EL0 is therefore the stack-pointer register that will be used at all EL3..EL0 exception levels.