Stack Exchange
Reading ICC_IAR1_EL1 register causes several exceptions?
I'm working on a bare-metal interrupt controller. The underlying architecture is Virt, with QEMU, and a CPU Arm Cortex-72, aarch64. As an example, I provide you an excerpt of the Makefile:
run:
    $(MAKE) kernel.elf
    qemu-system-aarch64 -machine virt -cpu cortex-a72 -nographic -kernel kernel.elf

I defined an exception handler that is called everytime an exception occurs. This is an excerpt (output is written on a UART):
void common_trap_handler(exception_frame *exc)
{ 
    uart_puts("\nException Handler! (");
    uint32_t val = raw_read_current_el();
    uart_puts("\n\tCurrent EL = ");
    uart_puthex(val);
    uart_puts("\t");

    uart_puts("exc_type : ");
    uart_puthex(exc->exc_type);
    
    val = raw_read_icc_iar1_el1();
    uart_puts("\n\tICC IAR1 EL1 = ");
    uart_puthex(val); 
    // rest of the exception handler
}

Accesses to registers are done through assembly code. Reading the current exception level works:
uint32_t raw_read_current_el(void)
{
    uint32_t current_el;
    __asm__ __volatile__("mrs %0, CurrentEL\n\t" : "=r" (current_el) :  : "memory");
    return current_el;
}

Reading ICC_IAR1_EL1 register does not (more details below):
uint32_t raw_read_icc_iar1_el1(void)
{
    uint32_t icc_iar1_el1 = 0;
    __asm__ __volatile__("mrs %0, s3_0_c12_c12_0\n\t" : "=r" (icc_iar1_el1) :  : "memory");
    return icc_iar1_el1; 
}

Where s3_0_c12_c12_0 is the ID of ICC_IAR1_EL1 according to arm specifications, since it is defined as a "register without an architectural name".
Getting access to ICC_IAR1_EL1 triggers a limitless number of other exceptions. This is an excerpt of the output (repeated several times):
Exception Handler! (
    Current EL = 0x00000000 00000004    exc_type : 0x00000000 00000011
Exception Handler! (
    Current EL = 0x00000000 00000004    exc_type : 0x00000000 00000011
Exception Handler! (
    Current EL = 0x00000000 00000004    exc_type : 0x00000000 00000011

I can't understand why. According to the name of the register ICC_IAR1_EL1, this register should be accessed with a minimum privilege of EL1. This is verified by the output Current EL = 0x00000000 00000004. Do you know how can I read the content of ICC_IAR1_EL1 register? Unfortunately, the official ARM guide did not help me

You need to specify that you want your qemu-virt machine to use a GICv3 in order to be able to use the register interface to the GIC, and not a GICv2 with a memory-mapped interface, which is the version of the GIC used by default by the qem-virt machine.
The following command is working fine:
qemu-system-aarch64 -machine virt,gic-version=3 -cpu cortex-a72 -nographic -kernel kernel.elf

My test program:
                .title minimal-aarch64.s
                .arch armv8-a
                .text
                .section .text.startup,"ax"    
                .globl _start
                .weak test
_start:
                ldr x0, =__StackTop
                mov sp, x0
                bl  test
wait:           wfe
                b wait

test:           
                mrs x0, s3_0_c12_c12_0
                ret
               .end

Using your command or
qemu-system-aarch64 -machine virt,gic-version=2 -cpu cortex-a72 -nographic -kernel kernel.elf

Does result in an exception being raised before the program returning from the test subroutine, which is the behaviour you observed:
Breakpoint 2, test () at minimal-aarch64.s:15
0x0000000000000200 in ?? ()


Dear Frant, I got the problem, thanks. I think there is a little issue in your answer: ,gic-version=3 did not work, gic_version=3 (with the underscore) has worked.

Thank you for the interesting feedback. I am using a vanilla qemu 5.2.0 I compiled from sources with the exact commands I provided. Using -machine virt,gic_version=3  gives the following error message on my system: Property 'virt-5.2-machine.gic_version' not found. You may be using a different version, which may explain the discrepancies. The documentation is using '-''as well.

The flavor of gic-version using an underscore instead of a caret was being referenced in a 2016 article on the Ubuntu Wiki. This was probably an accepted syntax until recently. Thanks again.

Thank you for clarifying this. Dear Frant, by placing gic_version=3 I can access to ICC_IAR1_EL1, but I am literally invaded by exceptions whose INTID is 1023. Basically, as far as I know, 1023 is a spurious interrupt, namely there are no enabled INTIDs in the pending state, or all INTIDs in that pending are of insufficient priority to be taken. I can't understand why I using this option gic_version=3 leads to receive lots of spurious interrupts. Did you face a similar problem?

What are you doing besides reading ICC_IAR1_EL1 ? I would need the exact sequence of operations you are using. Would you envisage opening a new question, and providing the minimal test program I used augmented by the exact sequence of operations your are using? I would definitively be willing to investigate your issue, and so may others.

Dear Frant, I come back to you with a question related to your answer. You told me "You need to specify that you want your qemu-virt machine to use a GICv3 in order to be able to use the register interface to the GIC, and not a GICv2 with a memory-mapped interface, which is the version of the GIC used by default by the qem-virt machine.". But, why can I read "CurrentEL" register without problems? Theorectically, it should raise the same issue of ICC_IAR1_EL1, non? Thanks

Hi Joe, I don't think so,  because CurrentEL is not related to the GIC sub-system, and is always present on an armv8-a processor. My understanding is that GIC-related registers are present in a processor only if it is implementing a GICv3 (or a GICv4). This is not always the case: you can  know if the GIC register interface is available by reading ID_AA64PFR0_EL1 - see section D12.2.56 ID_AA64PFR0_EL1, AArch64 Processor Feature Register 0 of the ARM® Architecture Reference Manual ARMv8, for ARMv8-A architecture profile.

mrs x0, ID_AA64PFR0_EL1 sets x0 to 0x00002222 with -machine virt,gic-version=2, and to 0x01002222 with -machine virt,gic-version=3. As per the documentation:
GIC, [27:24]
GIC CPU interface:
0x0 GIC CPU interface is disabled, GICCDISABLE is HIGH, or not implemented.
0x1 GIC CPU interface is implemented and enabled, GICCDISABLE is LOW.

See page 17 of GICv3 and GICv4 Software Overview: NOTE: Software can check for GIC System register support by reading ID_AA64PFR0_EL1for the PE, see ARM®Architecture Reference Manual, ARMv8, for ARMv8-A architecture profile for details.

Very very clear, thank you Frant! I think this discussion will be useful to others as well.
