Cover image for ARM Exception Model

ARM Exception Model

Timeline

Timeline

2025-10-16

init

This article introduces the exception model of the ARM64 architecture, discussing in detail exception levels, terminology definitions, and the classification of synchronous and asynchronous exceptions. It also summarizes the handling mechanisms of hardware and the operating system during exception entry and return phases, and explains the exception routing rules based on system configuration registers.

Reference documents:

Exception Levels of ARM64

  • EL0Unprivileged mode, e.g., applications
  • EL1Privileged mode, e.g., OS kernel
  • EL2Virtualization monitor, e.g., hypervisor
  • EL3Secure mode, e.g., secure monitor

Exception terminology

  • Taking an exception: Handling an exception
  • Returning from an exception: Returning from an exception
  • Exception levels: Exception level
  • Precise exception: Precise exception
  • Synchronous and asynchronous exception: Synchronous and asynchronous exceptions

Types of exceptions

  • Synchronous exceptions
    • System calls, SVC, HVC, SMC, etc.
    • Exceptions caused by the MMU
    • SP and PC alignment checks
    • Unallocated instructions
      • Unallocated instruction opcodes
      • Instructions requiring higher privilege than the current exception level.
      • Disabled instructions.
      • Any instruction when the PSTATE.IL field is set
    • Debug exceptions
  • Asynchronous exceptions
    • IRQ interrupts
    • FIQ interrupts
    • SError(System error)

Synchronous exceptions are exceptions that are triggered as expected, while asynchronous exceptions are triggered unexpectedly.

Exception Entry

Process state or PSTATE is an abstraction of process state information

  • When an exception occurs,the CPU hardwaredoes the following things
    • PSTATE is saved to SPSR_ELx (The Saved Program Status Register)
    • The return address is saved to ELR_ELx
    • The DAIF field of the PSTATE register is set to 1, which effectively disables debug exceptions, system errors (SError), IRQ interrupts, and FIQ interrupts.
    • The ESR_ELx (Exception Syndrome Register) is updated, containing the cause of the synchronous exception or SError.
    • SP switches to SP_ELx
    • It switches to the corresponding EL and then jumps to the exception vector table for execution.
  • What does the operating system need to do after an exception occurs?

The operating system needs to set up the exception vector table so that the CPU, based on the type of exception, jumps to the appropriate entry in the exception vector table.

Each entry in the exception vector table stores a jump function for exception handling, which then jumps to the appropriate exception handler to process the exception.

The Exception Syndrome Register (ESR_ELn) contains information that allows the exception handler to determine the cause of the exception. It is updated only for synchronous exceptions and SError, not for IRQ or FIQ, because those interrupt handlers typically obtain status information from the registers of the Generic Interrupt Controller (GIC).

Exception return

  • The operating system executes aeretstatement
    • Restore the PC pointer from the ELR_ELx (Exception Link Register) register
    • Restore the processor state from the SPSR_ELx (The Saved Program Status Register) register

Exception handling flow
Exception handling flow

Exception return address

  • Two registers for return address
    • x30: Return address of a subroutine. Use the ret instruction to return
    • ELR_ELx: Exception return address. Use the eret instruction to return
  • ELR_ELxThe register holds the exception return address
    • For asynchronous exceptions, the return address is the next instruction after the interrupt occurred, or the first instruction not executed
    • For synchronous exceptions that are not system calls, the return address is the instruction that triggered the synchronous exception
    • For a system call, it returns the next instruction after the svc/hvc instruction

Asynchronous exception (interrupt)

The hardware has already set ELR to_the next instruction to be executed_,eretthen it directly returns to continue execution without additional modification

Synchronous exception (non-system call)

The hardware sets ELR to_the instruction that triggered the exception_
If you fix the cause of the exception (e.g., a page fault), directly eretwill re-execute this instruction
If you want to skip it (without retrying), you must manuallyELR_ELx += 4

system call(svc)

The hardware sets ELR to_the next instruction after svc_, soeretwill directly return to the next one

Exception handling routing

When an exception occurs at a specific Exception Level (EL), which exception level the CPU should jump to in order to handle it

  • When an exception occurs, it can behandled at the current EL or at a higher EL
    • EL0 cannot be used to handle exceptions
    • Synchronous exceptions can be handled at the current EL, for example, when a synchronous exception occurs at EL1
    • Forasynchronous exceptions, they can be routed to EL1, EL2, or EL3 for handling, it is necessary to configureHCRthe Hypervisor Configuration Register andSCRthe Secure Configuration Register related registers
    • If an exception is generated by an instruction fetch at EL0, it is treated as an exception at EL1, unless the HCR_EL2.TGE bit is set for the Non-secure state, in which case it is treated as an exception at EL2.

To which level an exception should be routed when it occurs
To which level an exception should be routed when it occurs

Refer to Table D1-10 of the ARMv8.6 architecture

  • SCR_EL3: Secure Configuration Register
  • HCR_EL2: Hypervisor Configuration Register

The table in this image (Table D1-10) isthe interrupt/exception routing rule table when both EL3 (Secure Monitor mode) and EL2 (Virtualization Monitor mode) are implemented in the ARM architecture, core is used to define the target privilege level (EL0~EL3) that the processor ultimately jumps to after an interrupt (IRQ/FIQ) or exception (such as Abort exception) is triggered under different system states (register configurations).

Routing when both EL3 and EL2 are implemented
Routing when both EL3 and EL2 are implemented

  1. Row 1: SCR=0, NS EEL2a=0, EA IRQ FIQ=0, RW=0
  • Input conditions: Security configuration is default (SCR=0), Non-secure EL2 exception entry disabled (NS EEL2a=0), no active interrupts/exceptions (EA IRQ FIQ=0), read-write permission is 0; HCR TGE, E2H are arbitrary values (X).
  • Routing result
    • Triggered from EL0: jumps to “FIQ IRQ Abt” (i.e., the native handler entry for the corresponding interrupt/exception, not intercepted by a higher EL);
    • Triggered from EL1: same as EL0, jumps to “FIQ IRQ Abt”;
    • Triggered from EL2:n/a(Not applicable, as EL2 does not enable this scenario when NS EEL2a=0);
    • Triggered from EL3: jumps to “C” (usually refers to the exception handler entry in Secure state; in ARM architecture, “C” often represents the secure-related default target).
  1. Row 2: SCR=0, NS EEL2a=0, EA IRQ FIQ=0, RW=1
  • Input conditions: Only RW (read-write permission) is changed from 0 to 1, others are the same as Row 1.
  • Routing result
    • Triggered from EL0/EL1: jumps to “EL1” (i.e., the exception is intercepted and handled by EL1 (kernel mode), rather than the native interrupt entry, because RW=1 corresponds to higher-privilege kernel processing logic);
    • Triggered from EL2:n/a(Same as row 1, EL2 not enabled);
    • Triggered from EL3: Jump to ‘C’ (secure state entry unchanged).
  1. Row 3: SCR=0, NS EEL2a=0, EA IRQ FIQ=1
  • Input conditions: EA IRQ FIQ=1 (active interrupt/exception triggered), other configurations same as previous two rows; HCR TGE and E2H are arbitrary values.
  • Routing result
    • Triggered from EL0/EL1: Jump to ‘EL3’ (active interrupt/exception intercepted by the highest security level EL3, consistent with the ARM security architecture logic that ‘high-priority exceptions are handled by EL3’);
    • Triggered from EL2:n/a(EL2 not enabled);
    • Triggered from EL3: Jump to ‘EL3’ (exception triggered by EL3 itself, handled within EL3, no jump to other levels).
  1. Row 4: SCR=0, NS EEL2a=1, EA IRQ FIQ=0, HCR TGE=0, E2H=0, RW=0
  • Input conditions: NS EEL2a=1 (non-secure EL2 exception entry enabled), no active interrupts/exceptions (EA IRQ FIQ=0), virtualization mode disabled (HCR TGE=0), EL2 does not intercept EL1 exceptions (E2H=0).
  • Routing result
    • Triggered from EL0: Jump to “FIQ IRQ Abt” (native interrupt entry);
    • Triggered from EL1: Jump to “FIQ IRQ Abt” (EL2 does not intercept, because E2H=0);
    • Triggered from EL2: Jump to “C” (exception triggered from non-secure EL2, routed to secure entry “C”);
    • Triggered from EL3: Jump to “C” (handled internally in secure state, target unchanged).

Stack Selection

  • Each Exception Level (EL) has a corresponding stack pointer register SP_ELx
    • SP_EL0, SP_EL1, SP_EL2, SP_EL3
  • The stack must be 16-byte aligned. Hardware can detect whether the stack pointer is aligned
  • When an exception occurs and jumps to the target exception level, the hardware automatically selects SP_ELx
  • The operating system is responsible for allocationEnsuring that the stack corresponding to each Exception Level (EL) is available

Execution Mode

Execution Mode for Exception Handling

  • When an exception occurs, switching to a higher EL, which mode does this EL run in? AArch64 or AArch32
    • **HCR_EL2.RW(Hypervisor Configuration Register)**Records which mode EL1 should run in
      • 1 indicates aarch64
      • 0 indicates aarch32

Execution state control for lower Exception levels
Execution state control for lower Exception levels

  • After an exception occurs, the execution mode can change
    • An aarch32 application is running, and when an interrupt arrives, it may jump to EL1 in aarch64 execution state to handle the interrupt

Execution mode on exception return

  • When returning from an exception, the SPSR (Saved Program Status Register) records:
    • Which EL to return to?SPSR.M[3:0]
    • Execution mode of the target EL on return?SPSR.M[4]
      • 0 indicates aarch64
      • 1 indicates aarch32

Exceptions taken from AArch64 state
Exceptions taken from AArch64 state

M[3:0]Target modeDescription
0000EL0tEL0, using SP_EL0
0100EL1tEL1, using SP_EL0
0101EL1hEL1, using SP_EL1
1000EL2tEL2, using SP_EL0
1001EL2hEL2, using SP_EL2
1100EL3tEL3, using SP_EL0
1101EL3hEL3, using SP_EL3

Experiment 1: Switch to running in EL1

Experiment 1
Experiment 1

Hint

To switch from EL2 to EL1, the following things need to be done

  1. Set the HCR_EL2 (Hypervisor Configuration Register) register, the most important being the RW field at Bit 31, which indicates which execution environment EL1 will run in, aarch32 or aarch64(Similarly, the execution state of EL2 is determined by SCR_EL3)

    • HCR_EL2 belongs to the General System Control Register

    HCR_EL2
    HCR_EL2

    HCR_EL2.RW
    HCR_EL2.RW

  2. Set the SCTLR_EL1 (System Control Register), to set the endianness and disable the MMU

    SCTLR_EL1.EE
    SCTLR_EL1.EE

    SCTLR_EL1.E0E
    SCTLR_EL1.E0E

    SCTELR_EL1.M
    SCTELR_EL1.M

  3. Set the SPSR_EL2 (Saved Program Status Register) register, set the mode M field to EL1h, and also disable all DAIF of PSTATE

    • SPSR_ELxbelongs toSpecial-purpose Register

    SPSR_EL2
    SPSR_EL2

    SPSR_EL2.M
    SPSR_EL2.M

    PSTATE.DAIF at EL2
    PSTATE.DAIF at EL2


    SPSR_EL1
    SPSR_EL1

    SPSR_EL.M
    SPSR_EL.M

M[3:0]target modeDescription
0000EL0tEL0, using SP_EL0
0100EL1tEL1, using SP_EL0
0101EL1hEL1, using SP_EL1
1000EL2tEL2, using SP_EL0
1001EL2hEL2, using SP_EL2
1100EL3tEL3, using SP_EL0
1101EL3hEL3, using SP_EL3

PSTATE.DAIF at EL1
PSTATE.DAIF at EL1

  1. Set the exception return register elr_el2 to return to the el1_entry assembly function

  2. Executeeret

(3,4 are actually classic steps for exception return, returning from EL2 to EL1)

First, determine which exception level to jump to based on the current exception level

Determine the current exception level
Determine the current exception level

Note that the elr_el2 register must be set before executing eret

Jump to EL1
Jump to EL1

Exception vectors

  • Each exception level (EL) has its own exception vector table, except EL0

  • The base address of the exception vector table needs to be set in the**VBAR_ELx(Vector Base Address Register)**register

  • VBAR_EL1 register:

    VBAR_ELx
    VBAR_ELx

Bits 0~10 are reserved, so theaddress is 2KB aligned

Exception vector table structure
Exception vector table structure

  1. Row (Exception taken from)

Defines the “current execution state” when an exception is taken, divided into 4 scenarios:

  • Current Exception level with SP_EL0: The current exception level uses theSP_EL0stack pointer (e.g., an exception triggered when using the user-level stack pointer at EL1).
  • Current Exception level with SP_ELx, x>0: The current exception level uses theSP_ELx(x≥1, e.g., EL1 usesSP_EL1, EL2 usesSP_EL2triggers an exception when accessing kernel-level stack pointers, etc.).
  • Lower Exception level(AArch64): Triggered from a lower exception level, and the lower level is in AArch64 execution state (e.g., when switching from EL0/EL1 to a higher EL, and the original level is in 64-bit mode).
  • Lower Exception level(AArch32): Triggered from a lower exception level, and the lower level is in AArch32 execution state (e.g., switching from EL0/EL1 in 32-bit mode to a higher EL).
  1. Column (Offset for exception type)

Classified by exception type, corresponding to different offsets:

  • Synchronous: Synchronous exception (e.g., instruction execution errors, software-triggered exceptions, occurring synchronously with the instruction stream).
  • IRQ or vIRQ: Normal interrupt (IRQ) or virtual interrupt (vIRQ, in virtualization scenarios).
  • FIQ or vFIQ: Fast interrupt (FIQ, typically higher priority) or virtual fast interrupt (vFIQ, in virtualization scenarios).
  • SError or vSError: System error (SError, such as bus errors and other severe errors) or virtual system error (vSError, in virtualization scenarios).
  1. Offset values (e.g., 0x000, 0x080, etc.): Based on the combination of row (trigger state) and column (exception type), provides an offset relative to the vector table base address. The processor can find the entry address of the exception handler by base address + offset.

Exception Vector Table Structure
Exception Vector Table Structure

  • Overall Structure of the Exception Vector Table

InAArch64 EL1and above execution levels, the exception vector table (Vector Table) is stored in **VBAR_ELx**The address pointed to by the Vector Base Address Register.

This table contains a total of4 “blocks”, corresponding to different exception sources:

  1. **From the current SP (SP0)**triggered exception
  2. **From the current SP (SPx)**triggered exception (x≠0, e.g., SP_EL1)
  3. **From the next lower privilege level (e.g., EL0)**exception
  4. **From the current privilege level (e.g., within EL1)**exception

Each block further contains4 exception types

  • Synchronous exception(synchronous exceptions, e.g., SVC instruction, data access exceptions)
  • IRQ(Normal Interrupt)
  • FIQ(Fast Interrupt)
  • SError(System Error, usually a hardware exception)

So in total:
4 blocks × 4 exceptions = 16 entries


Size of each entry

ARMv8-A specifies:

  • Each entry is fixed at 128 bytes(0x80) (size of 32 instructions).
  • 16 entries × 128 bytes = 2048 bytes = 2KB
  • So the total size of the vector table is2KB

That is, the exception vector table must be2KB-alignedregion.

The exception vector table actually consists of 4 groups of 4 entries

Exception handling in the Linux 5.0 kernel

Exception vector table of Linux 5.0
Exception vector table of Linux 5.0

  • .align 11 aligns the exception vector table by2k size alignment

  • kernel_ventry is a macro, simplified code as follows

kernel_ventry
kernel_ventry

align 7 means alignment by 2 to the power of 7, which is 128 bytes

The sub instruction subtracts an S from the stack pointer sp_FRAME_SIZE, where S_FRAME_SIZE is called the register frame size, which is the size of the struct pt_regs data structure

“\()” indicates concatenation

For example: when an EL1 IRQ interrupt occurs, this statement becomes “b el1_irq”

kernel_entryis the first-level entry point jumped to by the exception vector table, and its responsibilities are:

  1. Save context: Save the CPU state (general-purpose registers) onto the stack.
  2. Establish stack frame: Prepare stack space for the exception handler (C code).
  3. Switch stack(If needed): For example, when transitioning from user mode to kernel mode, the stack must be switched to the kernel stack.
  4. Call the C-level exception dispatch function

Take the FIQ occurring at EL1 as an example

FIQ occurring at EL1
FIQ occurring at EL1

bad_mode
bad_mode

Save exception context

  • Stack frame: The Linux kernel defines astruct pt_regsdata structure to describe the arrangement of saved registers on the kernel stack, typically used to save interrupt context and other information.

Stack frame
Stack frame

orig_x0, syscallno, orig_addr_limit, unused, stackframe[0], stackframe[1] are all software-defined saved items.

Processing flow for saving exception context
Processing flow for saving exception context

Experiment 2: Set up an exception vector table and create a synchronous exception

Experiment 2
Experiment 2

FAR Fault Address Register (at EL1)

Its function is:

  • Whena synchronous exception occurs, the processor writes thevirtual addressthat caused the exception into theFAR_ELxcorresponding register.
  • EL1corresponds to the kernel mode (operating system level), soFAR_EL1stores thevirtual address at the time of the exception under EL1

Common scenarios

  1. Page Fault
    If the CPU triggers an exception when accessing a non-existent or illegal virtual address,FAR_EL1this address is saved, and the kernel can use it to decide whether to allocate a new page or kill the process.
  2. Alignment Fault
    If the access address does not comply with alignment rules,FAR_EL1that address will also be saved.
  3. Watchpoint/Breakpoint Exception
    FAR_EL1It may also be used to store the address that triggered the exception.

Related Registers

  • ESR_EL1(Exception Syndrome Register)
    Saves the exception’scause code(such as page fault, permission error, misalignment, etc.).
  • FAR_EL1
    Saves thespecific virtual address

entry.S

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#define BAD_SYNC  0
#define BAD_IRQ 1
#define BAD_FIQ 2
#define BAD_ERROR 3

.macro inv_entry el, reason
// Saves the exception context, not yet implemented
// kernel_entry el
mov x0, sp
mov x1, #\reason
mrs x2, esr_el1
b bad_mode
.endm


// Exception Vector Table
.macro vtentry label
.align 7
b \label
.endm

/*
* ARM64The exception vector table occupies a total of2048bytes,divided into four groups,Each entry occupies128bytes
* .align 11Indicates alignment by2048alignment
*/


.align 11
.global vectors

vectors:
/* Current EL with SP0
* The current system is running inEL1when usingEL0the stack pointer ofSP
* This is an abnormal error type
*/
vtentry el1_sync_invalid
vtentry el1_irq_invalid
vtentry el1_fiq_invalid
vtentry el1_error_invalid

/* Current EL with SPx
* The current system is running inEL1when usingEL1the stack pointer ofSP
* Indicates an exception occurred in the kernel mode of the system
* Currently only implementsIRQinterrupts
*/

vtentry el1_sync_invalid
vtentry el1_irq_invalid
vtentry el1_fiq_invalid
vtentry el1_error_invalid

/* Lower EL using AArch64
* user-modeaarch64An exception occurred in the program
*/

vtentry el0_sync_invalid
vtentry el0_irq_invalid
vtentry el0_fiq_invalid
vtentry el0_error_invalid


/* Lower EL using AArch32
* in user modeaarch32An exception occurred in the program
*/

vtentry el0_sync_invalid
vtentry el0_irq_invalid
vtentry el0_fiq_invalid
vtentry el0_error_invalid



el1_sync_invalid:
inv_entry 1, BAD_SYNC
el1_irq_invalid:
inv_entry 1, BAD_IRQ
el1_fiq_invalid:
inv_entry 1, BAD_FIQ
el1_error_invalid:
inv_entry 1, BAD_ERROR


el0_sync_invalid:
inv_entry 0, BAD_SYNC
el0_irq_invalid:
inv_entry 0, BAD_IRQ
el0_fiq_invalid:
inv_entry 0, BAD_FIQ
el0_error_invalid:
inv_entry 0, BAD_ERROR

// occupies two bytes
string_test:
.string "t"
// because string_test caused it to not be 4-byte aligned
.global trigger_alignment

trigger_alignment:
ldr x0, =0x80002
ldr x1, [x0]
ret

kernel.cdefined in

bad_mode
bad_mode

inkernel_maintriggered intrigger_alignment

trigger_alignment
trigger_alignment

inboot.Sset the exception vector table in

set the exception vector table
set the exception vector table

Experiment 3: Debug: Find the instruction that triggers an exception on Raspberry Pi 4

Experiment 3
Experiment 3

ldr a hong
ldr a hong

This instruction loads the value at address current PC + MY_LABEL into x6

Equivalent to:

1
x6 = *(PC-relative 地址 + MY_LABEL)

For LDR Xt,The target address must be 8-byte aligned (because Xt is a 64-bit register).

It can be changed to w6, requiring only 4-byte alignment, because MY_LABEL is defined as 0x20.

Exception Analysis

ESR_ELx (Exception Syndrome Register)

ESR_ELx is updated only for synchronous exceptions and SError, not for IRQ or FIQ, because these interrupt handlers typically obtain status information from the registers of the Generic Interrupt Controller (GIC).

ESR_ELx
ESR_ELx

  • The ESR register contains four fields in total.
    • Bits 32~63 are reserved bits.
    • Bits 26~31 are theException Class (EC), this fieldindicates the type of exception that occurred,and is also used toindex the ISS field.
    • Bit 25, IL, indicates the instruction length of a synchronous exception instruction, Instruction Length
    • Bit 0~24,ISS (Instruction Specific Syndrome) specific exception instruction encoding. This exception instruction encoding table depends on different exception types,different exception types have different encoding formats

Exception type in the ESR register
Exception type in the ESR register

Common exception type corresponding encodings

Instruction Abort
Instruction Abort

The first indicates an instruction exception from a lower exception level, the second indicates an instruction exception at the current exception level

Data Abort
Data Abort

Encoding method of the ISS field

ISS encoding for Instruction Abort
ISS encoding for Instruction Abort

ISS encoding for Data Abort
ISS encoding for Data Abort

ISS encoding format Data Abort
ISS encoding format Data Abort

Data Abort DFSC
Data Abort DFSC

FAR (Fault Address Register)

FAR
FAR

The FAR register storesthe virtual address at the time of the exception

Experiment 4: Parsing data exception information

Experiment 4
Experiment 4

Experiment 4 results
Experiment 4 results

esr.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#ifndef __ESR_H
#define __ESR_H

#define UL(x) x

#define ESR_ELx_EC_UNKNOWN (0x00)
#define ESR_ELx_EC_WFx (0x01)
/* Unallocated EC: 0x02 */
#define ESR_ELx_EC_CP15_32 (0x03)
#define ESR_ELx_EC_CP15_64 (0x04)
#define ESR_ELx_EC_CP14_MR (0x05)
#define ESR_ELx_EC_CP14_LS (0x06)
#define ESR_ELx_EC_FP_ASIMD (0x07)
#define ESR_ELx_EC_CP10_ID (0x08)
/* Unallocated EC: 0x09 - 0x0B */
#define ESR_ELx_EC_CP14_64 (0x0C)
/* Unallocated EC: 0x0d */
#define ESR_ELx_EC_ILL (0x0E)
/* Unallocated EC: 0x0F - 0x10 */
#define ESR_ELx_EC_SVC32 (0x11)
#define ESR_ELx_EC_HVC32 (0x12)
#define ESR_ELx_EC_SMC32 (0x13)
/* Unallocated EC: 0x14 */
#define ESR_ELx_EC_SVC64 (0x15)
#define ESR_ELx_EC_HVC64 (0x16)
#define ESR_ELx_EC_SMC64 (0x17)
#define ESR_ELx_EC_SYS64 (0x18)
/* Unallocated EC: 0x19 - 0x1E */
#define ESR_ELx_EC_IMP_DEF (0x1f)
#define ESR_ELx_EC_IABT_LOW (0x20)
#define ESR_ELx_EC_IABT_CUR (0x21)
#define ESR_ELx_EC_PC_ALIGN (0x22)
/* Unallocated EC: 0x23 */
#define ESR_ELx_EC_DABT_LOW (0x24)
#define ESR_ELx_EC_DABT_CUR (0x25)
#define ESR_ELx_EC_SP_ALIGN (0x26)
/* Unallocated EC: 0x27 */
#define ESR_ELx_EC_FP_EXC32 (0x28)
/* Unallocated EC: 0x29 - 0x2B */
#define ESR_ELx_EC_FP_EXC64 (0x2C)
/* Unallocated EC: 0x2D - 0x2E */
#define ESR_ELx_EC_SERROR (0x2F)
#define ESR_ELx_EC_BREAKPT_LOW (0x30)
#define ESR_ELx_EC_BREAKPT_CUR (0x31)
#define ESR_ELx_EC_SOFTSTP_LOW (0x32)
#define ESR_ELx_EC_SOFTSTP_CUR (0x33)
#define ESR_ELx_EC_WATCHPT_LOW (0x34)
#define ESR_ELx_EC_WATCHPT_CUR (0x35)
/* Unallocated EC: 0x36 - 0x37 */
#define ESR_ELx_EC_BKPT32 (0x38)
/* Unallocated EC: 0x39 */
#define ESR_ELx_EC_VECTOR32 (0x3A)
/* Unallocted EC: 0x3B */
#define ESR_ELx_EC_BRK64 (0x3C)
/* Unallocated EC: 0x3D - 0x3F */
#define ESR_ELx_EC_MAX (0x3F)

#define ESR_ELx_SET_SHIFT (11)
#define ESR_ELx_FnV_SHIFT (10)
#define ESR_ELx_EA_SHIFT (9)
#define ESR_ELx_CM_SHIFT (8)
#define ESR_ELx_S1PTW_SHIFT (7)
#define ESR_ELx_WNR_SHIFT (6)

#define ESR_ELx_EC_SHIFT (26)
#define ESR_ELx_EC_MASK (UL(0x3F) << ESR_ELx_EC_SHIFT)
#define ESR_ELx_EC(esr) (((esr) & ESR_ELx_EC_MASK) >> ESR_ELx_EC_SHIFT)

#define ESR_ELx_IL (UL(1) << 25)
#define ESR_ELx_ISS_MASK (ESR_ELx_IL - 1)
#define ESR_ELx_ISV (UL(1) << 24)
#define ESR_ELx_SAS_SHIFT (22)
#define ESR_ELx_SAS (UL(3) << ESR_ELx_SAS_SHIFT)
#define ESR_ELx_SSE_SHIFT (21)
#define ESR_ELx_SSE (UL(1) << 21)
#define ESR_ELx_SRT_SHIFT (16)
#define ESR_ELx_SRT_MASK (UL(0x1F) << ESR_ELx_SRT_SHIFT)
#define ESR_ELx_SF_SHIFT (15)
#define ESR_ELx_SF (UL(1) << 15)
#define ESR_ELx_AR_SHIFT (14)
#define ESR_ELx_AR (UL(1) << 14)
#define ESR_ELx_EA (UL(1) << 9)
#define ESR_ELx_CM (UL(1) << 8)
#define ESR_ELx_S1PTW (UL(1) << 7)
#define ESR_ELx_WNR (UL(1) << 6)
#define ESR_ELx_FSC (0x3F)
#define ESR_ELx_FSC_TYPE (0x3C)
#define ESR_ELx_FSC_EXTABT (0x10)
#define ESR_ELx_FSC_FAULT (0x04)
#define ESR_ELx_FSC_PERM (0x0C)
#define ESR_ELx_CV (UL(1) << 24)
#define ESR_ELx_COND_SHIFT (20)
#define ESR_ELx_COND_MASK (UL(0xF) << ESR_ELx_COND_SHIFT)
#define ESR_ELx_WFx_ISS_WFE (UL(1) << 0)
#define ESR_ELx_xVC_IMM_MASK ((1UL << 16) - 1)

#endif

Function: Parse the value of the ESR_ELx register to facilitate exception handling.

Structure

  • High 6 bits →EC(Exception Class)
  • IL, ISV → Instruction length/validity
  • ISS → Instruction Specific Syndrome
  • FSC → Data access exception code
  • WNR, EA, CM → Access attributes

kernel.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
static const char *const bad_mode_handler[] = {"Sync Abort", "IRQ", "FIQ",
"SError"};
static const char *data_fault_code[] = {
[0] = "Address size fault, level0",
[1] = "Address size fault, level1",
[2] = "Address size fault, level2",
[3] = "Address size fault, level3",
[4] = "Translation fault, level0",
[5] = "Translation fault, level1",
[6] = "Translation fault, level2",
[7] = "Translation fault, level3",
[9] = "Access flag fault, level1",
[10] = "Access flag fault, level2",
[11] = "Access flag fault, level3",
[13] = "Permission fault, level1",
[14] = "Permission fault, level2",
[15] = "Permission fault, level3",
[0x21] = "Alignment fault",
[0x35] = "Unsupported Exclusive or Atomic access",
};

static const char *esr_get_dfsc_string(unsigned int esr) {
return data_fault_code[esr & 0x3f];
}

static const char *esr_class_str[] = {
// GCC extension, indicating array indices from 0 to ESR_ELx_EC_MAX all initialized to "UNRECOGNIZED
// EC"。
[0 ... ESR_ELx_EC_MAX] = "UNRECOGNIZED EC",
[ESR_ELx_EC_UNKNOWN] = "Unknown/Uncategorized",
[ESR_ELx_EC_WFx] = "WFI/WFE",
[ESR_ELx_EC_CP15_32] = "CP15 MCR/MRC",
[ESR_ELx_EC_CP15_64] = "CP15 MCRR/MRRC",
[ESR_ELx_EC_CP14_MR] = "CP14 MCR/MRC",
[ESR_ELx_EC_CP14_LS] = "CP14 LDC/STC",
[ESR_ELx_EC_FP_ASIMD] = "ASIMD",
[ESR_ELx_EC_CP10_ID] = "CP10 MRC/VMRS",
[ESR_ELx_EC_CP14_64] = "CP14 MCRR/MRRC",
[ESR_ELx_EC_ILL] = "PSTATE.IL",
[ESR_ELx_EC_SVC32] = "SVC (AArch32)",
[ESR_ELx_EC_HVC32] = "HVC (AArch32)",
[ESR_ELx_EC_SMC32] = "SMC (AArch32)",
[ESR_ELx_EC_SVC64] = "SVC (AArch64)",
[ESR_ELx_EC_HVC64] = "HVC (AArch64)",
[ESR_ELx_EC_SMC64] = "SMC (AArch64)",
[ESR_ELx_EC_SYS64] = "MSR/MRS (AArch64)",
[ESR_ELx_EC_IMP_DEF] = "EL3 IMP DEF",
[ESR_ELx_EC_IABT_LOW] = "IABT (lower EL)",
[ESR_ELx_EC_IABT_CUR] = "IABT (current EL)",
[ESR_ELx_EC_PC_ALIGN] = "PC Alignment",
[ESR_ELx_EC_DABT_LOW] = "DABT (lower EL)",
[ESR_ELx_EC_DABT_CUR] = "DABT (current EL)",
[ESR_ELx_EC_SP_ALIGN] = "SP Alignment",
[ESR_ELx_EC_FP_EXC32] = "FP (AArch32)",
[ESR_ELx_EC_FP_EXC64] = "FP (AArch64)",
[ESR_ELx_EC_SERROR] = "SError",
[ESR_ELx_EC_BREAKPT_LOW] = "Breakpoint (lower EL)",
[ESR_ELx_EC_BREAKPT_CUR] = "Breakpoint (current EL)",
[ESR_ELx_EC_SOFTSTP_LOW] = "Software Step (lower EL)",
[ESR_ELx_EC_SOFTSTP_CUR] = "Software Step (current EL)",
[ESR_ELx_EC_WATCHPT_LOW] = "Watchpoint (lower EL)",
[ESR_ELx_EC_WATCHPT_CUR] = "Watchpoint (current EL)",
[ESR_ELx_EC_BKPT32] = "BKPT (AArch32)",
[ESR_ELx_EC_VECTOR32] = "Vector catch (AArch32)",
[ESR_ELx_EC_BRK64] = "BRK (AArch64)",
};

static const char *esr_get_class_string(unsigned int esr) {
return esr_class_str[esr >> ESR_ELx_EC_SHIFT];
}

void parse_esr(unsigned int esr) {
unsigned int ec = ESR_ELx_EC(esr);

printk("ESR info:\n");
printk(" ESR = 0x%08x\n", esr);
printk(" Exception class = %s, IL = %u bits\n", esr_get_class_string(esr),
(esr & ESR_ELx_IL) ? 32 : 16);

if (ec == ESR_ELx_EC_DABT_LOW || ec == ESR_ELx_EC_DABT_CUR) {
printk(" Data abort:\n");
if ((esr & ESR_ELx_ISV)) {
printk(" Access size = %u byte(s)\n",
1U << ((esr & ESR_ELx_SAS) >> ESR_ELx_SAS_SHIFT));
printk(" SSE = %lu, SRT = %lu\n",
(esr & ESR_ELx_SSE) >> ESR_ELx_SSE_SHIFT,
(esr & ESR_ELx_SRT_MASK) >> ESR_ELx_SRT_SHIFT);
printk(" SF = %lu, AR = %lu\n", (esr & ESR_ELx_SF) >> ESR_ELx_SF_SHIFT,
(esr & ESR_ELx_AR) >> ESR_ELx_AR_SHIFT);
}

printk(" SET = %lu, FnV = %lu\n", (esr >> ESR_ELx_SET_SHIFT) & 3,
(esr >> ESR_ELx_FnV_SHIFT) & 1);
printk(" EA = %lu, S1PTW = %lu\n", (esr >> ESR_ELx_EA_SHIFT) & 1,
(esr >> ESR_ELx_S1PTW_SHIFT) & 1);
printk(" CM = %lu, WnR = %lu\n", (esr & ESR_ELx_CM) >> ESR_ELx_CM_SHIFT,
(esr & ESR_ELx_WNR) >> ESR_ELx_WNR_SHIFT);
printk(" DFSC = %s\n", esr_get_dfsc_string(esr));
} else {
printk("Not supported yet\n");
}
}

void bad_mode(struct pt_regs *regs, int reason, unsigned int esr) {
printk("Bad mode for %s handler detected, far:0x%x esr:0x%x - %s\n",
bad_mode_handler[reason], read_sysreg(far_el1), esr,
esr_get_class_string(esr));

parse_esr(esr);
}

extern void trigger_sync_data_abort(void);
extern void trigger_sync_instruction_alignment(void);

Code that triggers a data exception:

Trigger a synchronous data exception
Trigger a synchronous data exception