Cover image for ARM Exception Model

ARM Exception Model

Words 4.5k
Views
Visitors
Timeline

Timeline

2025-10-16

init

This article introduces the exception model in the ARM64 architecture, including the definition of exception levels (EL0~EL3), exception-related terminology, the classification of synchronous and asynchronous exceptions, and the hardware and software processing flow of exception entry and return. The article details the state saving automatically completed by the CPU hardware when an exception occurs (e.g., PSTATE is saved to SPSR_ELx, return address saved to ELR_ELx, update ESR_ELx, etc.), and the operating system needs to set up the exception vector table and exception handling functions. At the same time, the article summarizes three cases of exception return addresses (asynchronous exceptions, non-system-call synchronous exceptions, system calls), and introduces the routing rules for exception handling, including that synchronous exceptions can be handled at the current EL, asynchronous exceptions can be routed to EL1/EL2/EL3, and HCR_The influence of EL2 and SCR_EL3 registers on routing configuration. Finally, the article cites Table D1-10 in the ARMv8.6 specification, illustrating the target privilege levels of exceptions under different configurations.

Reference documents:

Exception levels of ARM64 (Exception Level)

  • EL0 Non-privileged mode, e.g., applications
  • EL1 Privileged mode, e.g., OS kernel
  • EL2 Virtualization monitor, e.g., hypervisor
  • EL3 Secure mode, e.g., secure monitor

Terminology (Exception terminology)

  • Taking an exception: The CPU responds to an exception (traps to exception handling)
  • Returning from an exception: Return from an exception
  • Exception levels: Exception level
  • Precise exception: Precise exception
  • Synchronous and asynchronous exception: Synchronous and asynchronous exceptions

Types of exceptions

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

Synchronous exceptions are predictable and reproducible exceptions triggered by the execution of a specific instruction; asynchronous exceptions are unpredictable exceptions triggered by external events (such as interrupts).

Exception Entry

Process state or PSTATE is an abstraction of process state information

  • When an exception occurs,CPU hardwarewhat does it do?
    • PSTATE is saved to SPSR_ELx (The Saved Program Status Register)
    • The return address is saved to ELR_ELx
    • The DAIF fields of the PSTATE register are all 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 reason for the synchronous exception or SError.
    • SP is switched to SP_ELx
    • Switch to the corresponding EL, then jump to the exception vector table for execution.
  • After an exception occurs,operating systemwhat does it need to do?

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

Each entry in the exception vector table stores a branch instruction (e.g.,b <handler>), which jumps to the corresponding exception handler.

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 these interrupt handlers typically obtain status information from the Generic Interrupt Controller (GIC) registers.

Exception return

  • The operating system executes aneretstatement
    • 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 return address registers
    • x30: return address of the subroutine. Use the ret instruction to return.
    • ELR_ELx: exception return address. Use the eret instruction to return.
  • ELR_ELxThe register stores the exception return address.
    • For asynchronous exceptions, the return address is the next instruction when the interrupt occurs, or the first instruction that was not executed.
    • For synchronous exceptions that are not system calls, the return address is the instruction that triggered the synchronous exception.
    • For system calls, 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 executederetJust go back and continue executing; no additional modification is needed.

Synchronous exception (not a system call)

The hardware sets ELR to the instruction that triggered the exception
If you have fixed the cause of the exception (e.g., a page fault), then eretit will directly re-execute this instruction.
If you want to skip it (do not retry), you need to manuallyELR_ELx += 4

system call(svc)

The hardware sets ELR to the next instruction after svc, soeretit will directly return to the next instruction.

Exception handling routing

When an exception occurs at a specific “Exception Level (EL)”, to which Exception Level should the CPU jump to handle it?

  • When an exception occurs, it can behandled at the current EL, or it can be handled 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 can be routed to EL1, EL2, or EL3 for handling., which requires configuringHCR(Hypervisor Configuration Register) andSCRregisters related to the Secure Configuration Register.
    • If the exception is caused by an instruction fetch at EL0, it is treated as an EL1 exception, unless the HCR_EL2.TGE bit is set to 1 and the processor is in a non-secure state, in which case it is treated as an EL2 exception.

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

Refer to Table D1-10 in ARMv8.6.

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

The table in this figure (Table D1-10) isthe interrupt/exception routing rule table for the ARM architecture when EL3 (Secure Monitor Mode) and EL2 (Virtualization Monitor Mode) are implemented simultaneously., and is mainly used to define the target privilege level (EL0~EL3) to which the processor ultimately jumps after an interrupt (IRQ/FIQ) or exception (such as an 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 is disabled (NS EEL2a=0), no active interrupts/exceptions (EA IRQ FIQ=0), read/write permission is 0; HCR TGE and E2H are arbitrary values (X).
  • Routing result
    • Triggered from EL0: jumps to “FIQ IRQ Abt” (i.e., the native handling 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, because when NS EEL2a=0, EL2 does not enable this scenario);
    • Triggered from EL3: jump to ‘C’ (usually refers to the exception handling 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) changes from 0 to 1; the rest is the same as Row 1.
  • Routing result
    • Triggered from EL0/EL1: jump 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 handling 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 (an active interrupt/exception is triggered), other configurations are the same as the previous two rows; HCR TGE and E2H are arbitrary values.
  • Routing result
    • Triggered from EL0/EL1: jump to ‘EL3’ (active interrupt/exception is 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’ (an exception triggered by EL3 itself is handled within EL3, without jumping 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 interrupt/exception (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’ (an exception triggered by Non-secure EL2 is routed to the Secure state 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.**The 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 allocatingand ensuring that the stack corresponding to each exception level EL is available.

Execution mode

Execution mode for exception handling

  • When an exception occurs, it switches 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 means aarch64
      • 0 means 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. At this time, an interrupt arrives, and it may jump to EL1 in the aarch64 execution state to handle the interrupt.

Execution mode for exception return

  • When returning from an exception, the SPSR register (Saved Program Status Register) records:
    • Which EL to return to? SPSR.M[3:0]
    • The execution mode of the target EL to return to?SPSR.M[4]
      • 0 means aarch64
      • 1 means 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, you need to do the following things:

  1. Set the HCR_EL2 (Hypervisor Configuration Register) register. The most important thing is the RW field at bit 31, which indicates which execution state EL1 will run in, AArch32 or AArch64.(Similarly, the execution state of EL2 is determined by SCR_EL3.)

    • HCR_EL2 is a General System Control Register.

    HCR_EL2
    HCR_EL2

    HCR_EL2.RW
    HCR_EL2.RW

  2. Set 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

    SCTLR_EL1.M
    SCTLR_EL1.M

  3. Set the SPSR_EL2 (Saved Program Status Register) register, set the mode M field to EL1h, and also disable all DAIF in 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 so that it returns to the el1_entry assembly function.

  2. Executeeret

(3, 4 are actually the classic steps of an exception return, from EL2 back 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 vector table (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 to VBAR_ELx(Vector Base Address Register) the registers

  • VBAR_EL1 register:

    VBAR_ELx
    VBAR_ELx

    Bits 0-10 are reserved, thereforeThe address is aligned to 2K.

Exception vector table structure
Exception vector table structure

  1. Row (Exception taken from)

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

    • Current Exception level with SP_EL0: The current exception level usesSP_EL0stack pointer (e.g., when an exception is triggered while using the user-level stack pointer at EL1).
    • Current Exception level with SP_ELx, x>0: The current exception level usesSP_ELx(x≥1, e.g., EL1 usesSP_EL1, EL2 usesSP_EL2etc., kernel-level stack pointer) when an exception is triggered.
    • 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 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).
  2. Column (Offset for exception type)

    Classified by exception type, corresponding to different offsets:

    • Synchronous: Synchronous exception (e.g., instruction execution errors, software-triggered exceptions, etc., 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, usually higher priority) or virtual fast interrupt (vFIQ, virtualization scenarios).
    • SError or vSError: System error (SError, e.g., serious errors like bus errors) or virtual system error (vSError, virtualization scenarios).
  3. Offset value (e.g., 0x000, 0x080, etc.): Based on the combination of row (trigger state) and column (exception type), the offset relative to the vector table base address is given. 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

Before AArch64 EL1 In execution levels at and above, the exception vector table (Vector Table) is stored at VBAR_ELx (Vector Base Address Register) register pointed to address.

This table contains a total of 4 “blocks” (blocks), corresponding to different exception sources:

  1. From the current SP (SP0) triggered exceptions
  2. From the current SP (SPx) triggered exceptions (x≠0, e.g., SP_EL1)
  3. From a lower privilege level using AArch64 (e.g., EL0) exceptions
  4. From a lower privilege level using AArch32 (e.g., EL0) exceptions

Each block also contains 4 exception types

  • Synchronous exception (synchronous exceptions, e.g., SVC instructions, data access exceptions)
  • IRQ (normal interrupt)
  • FIQ (fast interrupt)
  • SError (system error, usually a hardware exception)

So in total:
4 blocks × 4 exception types = 16 table entries


Size of each entry

ARMv8-A specifies:

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

In other words, the exception vector table must be 2KB 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 is to make the exception vector table2K-aligned

  • kernel_ventry is a macro, the simplified code is as follows

kernel_ventry
kernel_ventry

align 7 means aligning 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.

“\()” means concatenation.

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

kernel_entryIt is the first-level entry that the exception vector table jumps to, and its responsibilities are:

  1. Save context: Save the CPU state (general-purpose registers) to the stack.
  2. Set up stack frame: Prepare stack space for the exception handling function (C code).
  3. Switch Stack(If needed): For example, when entering kernel mode from user mode, the stack needs to be switched to the kernel stack.
  4. Call the C-layer 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: In the Linux kernel, a … is defined. struct pt_regs A data structure that describes the arrangement of registers saved on the kernel stack, typically used to save exception context.

Stack frame
Stack frame

orig_x0, syscallno, orig_addr_limit, unused, stackframe[0], stackframe[1] are all things defined and saved by software

Handling process for saving exception context
Handling process for saving exception context

Experiment 2: Build an exception vector table and create a synchronous exception

Experiment 2
Experiment 2

FAR Fault Address Register (at EL1)

Its function is:

  • when Synchronous Exception occurred when, the processor will [handle] the exception-causing… virtual addresswrite toFAR_ELxIn the corresponding register.
  • EL1It corresponds to the kernel mode (operating system layer), soFAR_EL1What is saved is Virtual address when an exception occurs at EL1

Common Scenarios

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

Related registers

  • ESR_EL1(Exception Syndrome Register)
    Save the exception’s cause code(such as page fault, permission error, misalignment, etc.).
  • FAR_EL1
    Save the specific virtual address

entry.S

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
#define BAD_SYNC  0#define BAD_IRQ   1#define BAD_FIQ   2#define BAD_ERROR 3    .macro inv_entry el, reason    // Save 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 that when2048Alignment*/.align 11.global vectorsvectors:/* Current EL with SP0*  using the current exception level SP_EL0 an exception is triggered(For example EL1 use SP_EL0)*/  vtentry el1_sync_invalid  vtentry el1_irq_invalid  vtentry el1_fiq_invalid  vtentry el1_error_invalid/* Current EL with SPx* the system is currently running inEL1used whenEL1the stack pointerSP* indicates that an exception occurred in the kernel mode of the system* currently only implementsIRQinterrupt*/  vtentry el1_sync_invalid  vtentry el1_irq_invalid  vtentry el1_fiq_invalid  vtentry el1_error_invalid/* Lower EL using AArch64* user-modeaarch64program exception occurs*/  vtentry el0_sync_invalid  vtentry el0_irq_invalid  vtentry el0_fiq_invalid  vtentry el0_error_invalid/* Lower EL using AArch32* in user modeaarch32program exception occurs*/  vtentry el0_sync_invalid  vtentry el0_irq_invalid  vtentry el0_fiq_invalid  vtentry el0_error_invalidel1_sync_invalid:  inv_entry 1, BAD_SYNCel1_irq_invalid:  inv_entry 1, BAD_IRQel1_fiq_invalid:  inv_entry 1, BAD_FIQel1_error_invalid:  inv_entry 1, BAD_ERRORel0_sync_invalid:  inv_entry 0, BAD_SYNCel0_irq_invalid:  inv_entry 0, BAD_IRQel0_fiq_invalid:  inv_entry 0, BAD_FIQel0_error_invalid:  inv_entry 0, BAD_ERROR// occupies two bytesstring_test:  .string "t"// because string_test causes it to be not 4-byte aligned.global trigger_alignmenttrigger_alignment:  ldr x0, =0x80002  ldr x1, [x0]  ret

kernel.cdefined in

bad_mode
bad_mode

Beforekernel_maintriggered intrigger_alignment

trigger_alignment
trigger_alignment

Beforeboot.Sset up the exception vector table in

set up the exception vector table
set up the exception vector table

Experiment 3: Fix the bug: Find the instruction that triggers the exception on Raspberry Pi 4

Experiment 3
Experiment 3

ldr a hong
ldr a hong

This instruction loads the value at the address 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, which only requires 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 a total of four fields (domains).
    • Bits 32~63 are reserved bits.
    • Bits 26~31 areException Class (EC for short), this fieldIndicates the type of exception that occurred, and is also used toIndex the ISS field
    • Bit 25, IL, indicates the instruction length of the synchronous exception instruction, Instruction Length
    • Bit 0~24,ISS (Instruction Specific Syndrome) specific exception instruction encoding. This exception instruction encoding table depends on the 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

FAR registerStores the virtual address at the time the exception occurred

Experiment 4: Parsing data abort information

Experiment 4
Experiment 4

Experiment 4 results
Experiment 4 results

esr.h

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
#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)/* Unallocated 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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
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 that the 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 data exception:

Trigger sync data exception
Trigger sync data exception

Loading comments…