Search code examples
gcccpu-registersarm64gnu-assemblerinterrupt-handling

ARM64 (Cortex-A53) - GNU Assembler - GIC register: unknown or missing system register name


I have started a simple bare-metal application for the Cortex-A53. Now I want implement interrupts, but I run into an issue. Want to read the registers ICC_SRE_ELx to determine the SRE flag, to know if I must use the memory-mapped GIC interface. Want also write to these register, if SRE is enabled, to enable IRQ's and FIQ's.

Got these error messages:

$ make
aarch64-suse-linux-gcc -c -Wall -I ./include -ffreestanding -mcpu=cortex-a53 misc.S -o misc.o
misc.S: Assembler messages:
misc.S:38: Error: unknown or missing system register name at operand 2 -- `mrs x0,ICC_SRE_EL2'
misc.S:42: Error: unknown or missing system register name at operand 2 -- `mrs w0,ICC_SRE_EL2'
misc.S:44: Error: unknown or missing system register name at operand 1 -- `msr ICC_SRE_EL2,w0'
misc.S:48: Error: unknown or missing system register name at operand 2 -- `mrs x0,ICC_SRE_EL2'
misc.S:50: Error: unknown or missing system register name at operand 2 -- `mrs x0,ICC_SRE_EL2'

Wrote this simple code:

#include <asm.h>

#define ICC_SRE_EL2_FIQ     0x2
#define ICC_SRE_EL2_IRQ     0x4

.text

FUNCTION(_cpu_get_el)
    mrs x0, CurrentEL
    and x0, x0, #0xC
    asr x0, x0, #2
    ret

FUNCTION(_cpu_get_id)
    mrs x0, MPIDR_EL1
    and x0, x0, #0x3
    ret

FUNCTION(_cpu_get_icc_sre_el2)
    mrs x0, ICC_SRE_EL2
    ret

FUNCTION(_cpu_set_icc_sre_el2_irq)
    mrs x0, ICC_SRE_EL2
    orr x0, x0, #ICC_SRE_EL2_IRQ
    msr ICC_SRE_EL2, x0
    ret

FUNCTION(_cpu_set_icc_sre_el2_fiq)
    mrs x0, ICC_SRE_EL2
    orr x0, x0, #ICC_SRE_EL2_FIQ
    mrs x0, ICC_SRE_EL2
    ret

.end

I use the following GCC flags:

-Wall -I ./include -ffreestanding -mcpu=cortex-a53

According to the official TRM, these registers should be implemented on the Cortex-A53.

I'm new on this architecture. Any help would be appreciated!

EDIT:

I have tried the following GAS versions:

The package from my used OS (openSUSE Leap 15.1):

$ aarch64-suse-linux-as --version
GNU assembler (GNU Binutils; devel:gcc / openSUSE_Leap_15.1) 2.34.0.20200325-lp151.386
Copyright (C) 2020 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or later.
This program has absolutely no warranty.
This assembler was configured for a target of `aarch64-suse-linux'.

The official tool-chains from the ARM homepage:

$ ../gcc/bin/aarch64-none-elf-as --version
GNU assembler (GNU Toolchain for the A-profile Architecture 9.2-2019.12 (arm-9.10)) 2.33.1.20191209
Copyright (C) 2019 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or later.
This program has absolutely no warranty.
This assembler was configured for a target of `aarch64-none-elf'.

Solution

  • GNU AS is not aware of all Aarch64 symbolic system registers names, you would need to replace ICC_SRE_EL2 by its op0,op1,CRn,CRm,op2 encoding, i.e. s3_4_c12_c9_5 - please refer to the Arm documentation here (look for the 'Accessing the ICC_SRE_EL2' section).

    Those registers can of course be accessed directly from C/C++ code using utility functions such as those provided hereafter:

    // write system register  ICC_SRE_EL1 (s3_0_c12_c12_5) with specified value.
    static inline void system_write_ICC_SRE_EL1(uint64_t val)
    {
        asm volatile("msr s3_0_c12_c12_5 , %0" : : "r" (val));
    }
    
    // read system register value ICC_SRE_EL1 (s3_0_c12_c12_5).
    static inline uint64_t system_read_ICC_SRE_EL1(void)
    {
        uint64_t val;
        asm volatile("mrs %0, s3_0_c12_c12_5" : "=r" (val));
        return val;
    }
    
    // write system register  ICC_SRE_EL2 (s3_4_c12_c9_5) with specified value.
    static inline void system_write_ICC_SRE_EL2(uint64_t val)
    {
        asm volatile("msr s3_4_c12_c9_5 , %0" : : "r" (val));
    }
    
    // read system register value ICC_SRE_EL2 (s3_4_c12_c9_5).
    static inline uint64_t system_read_ICC_SRE_EL2(void)
    {
        uint64_t val;
        asm volatile("mrs %0, s3_4_c12_c9_5" : "=r" (val));
        return val;
    }
    
    // write system register  ICC_SRE_EL3 (s3_6_c12_c12_5) with specified value.
    static inline void system_write_ICC_SRE_EL3(uint64_t val)
    {
        asm volatile("msr s3_6_c12_c12_5 , %0" : : "r" (val));
    }
    
    // read system register value ICC_SRE_EL3 (s3_6_c12_c12_5).
    static inline uint64_t system_read_ICC_SRE_EL3(void)
    {
        uint64_t val;
        asm volatile("mrs %0, s3_6_c12_c12_5" : "=r" (val));
        return val;
    }
    

    The list of system registers recognized by GNU AS can be found in variable const aarch64_sys_reg aarch64_sys_regs [], defined in the opcodes/aarch64-opc.c GNU AS source code file part of the archive downloadable from here:

    /* TODO there is one more issues need to be resolved
       1. handle cpu-implementation-defined system registers.  */
    const aarch64_sys_reg aarch64_sys_regs [] =
    {
      { "spsr_el1",         CPEN_(0,C0,0),  0 }, /* = spsr_svc */
      { "spsr_el12",    CPEN_ (5, C0, 0), F_ARCHEXT },
      { "elr_el1",          CPEN_(0,C0,1),  0 },
      { "elr_el12", CPEN_ (5, C0, 1), F_ARCHEXT },
      { "sp_el0",           CPEN_(0,C1,0),  0 },
      { "spsel",            CPEN_(0,C2,0),  0 },
      { "daif",             CPEN_(3,C2,1),  0 },
      { "currentel",        CPEN_(0,C2,2),  F_REG_READ }, /* RO */
      { "pan",      CPEN_(0,C2,3),  F_ARCHEXT },
      { "uao",      CPEN_ (0, C2, 4), F_ARCHEXT },
      { "nzcv",             CPEN_(3,C2,0),  0 },
      { "ssbs",     CPEN_(3,C2,6),  F_ARCHEXT },
      { "fpcr",             CPEN_(3,C4,0),  0 },
      { "fpsr",             CPEN_(3,C4,1),  0 },
      { "dspsr_el0",        CPEN_(3,C5,0),  0 },
      { "dlr_el0",          CPEN_(3,C5,1),  0 },
      { "spsr_el2",         CPEN_(4,C0,0),  0 }, /* = spsr_hyp */
      { "elr_el2",          CPEN_(4,C0,1),  0 },
      { "sp_el1",           CPEN_(4,C1,0),  0 },
      { "spsr_irq",         CPEN_(4,C3,0),  0 },
      { "spsr_abt",         CPEN_(4,C3,1),  0 },
      { "spsr_und",         CPEN_(4,C3,2),  0 },
      { "spsr_fiq",         CPEN_(4,C3,3),  0 },
      { "spsr_el3",         CPEN_(6,C0,0),  0 },
      { "elr_el3",          CPEN_(6,C0,1),  0 },
      { "sp_el2",           CPEN_(6,C1,0),  0 },
      ...
    

    I hope this help.