Search code examples
assemblyarmarm64

I can't reset the value of a GICv3-related register?


I'm working on a bare-metal interrupt controller, GIC version 3. The underlying architecture is Virt, with QEMU, and a CPU Arm Cortex-72, aarch64. I run my project with the following command:

qemu-system-aarch64 -machine virt,gic-version=3 -cpu cortex-a72 -nographic -kernel kernel.elf

I can't reset the value of GIC_GICC_BPR1_EL1, but I can set it to any other value. I prepared a test function just below:

void test(void) {
    uint64_t state = 0;

    // set Binary point to 0x07
    SysReg_write_ICC_BPR1_EL1(0x07);
    // read Binary point    
    state = SysReg_read_ICC_BPR1_EL1();
    uart_puts("TEST - ICC_BPR1_EL1 (expected 0x7): ");
    uart_puthex(state);
    uart_puts("\n");

    // set Binary point to 0
    SysReg_write_ICC_BPR1_EL1(0x00);

    // read Binary point    
    state = SysReg_read_ICC_BPR1_EL1();
    uart_puts("TEST - ICC_BPR1_EL1 (expected 0): ");
    uart_puthex(state);
    uart_puts("\n");  
}

Functions for reading and setting are:

uint64_t SysReg_read_ICC_BPR1_EL1(void)
{
    uint64_t val;
    asm volatile("mrs %0, s3_0_c12_c12_3" : "=r" (val));
    return val;
}

// write system register  ICC_BPR1_EL1 (s3_0_c12_c12_3) with specified value.
// source: /home/user/git/preprocessor/arm/SysReg_xml_v87A-2020-09/AArch64-icc_bpr1_el1.xml
void SysReg_write_ICC_BPR1_EL1(uint64_t val)
{
    asm volatile("msr s3_0_c12_c12_3 , %0" : : "r" (val));
}

Where s3_0_c12_c12_3is the ID of the register according to arm specifications, since GICC-related registers are defined as a "registers without an architectural name".

The output of this example is:

TEST - ICC_BPR1_EL1 (expected 0x7): 0x00000000 00000007
TEST - ICC_BPR1_EL1 (expected 0): 0x00000000 00000001

I used an uart, but you can use gdb as well, by taking the assembly code of void test(void) function and using labels.

So, I can't set it to 0 in any way. Do you know where am I wrong?


Solution

  • I was unable to reproduce your problem, your functions are working fine on my setup. However, I may have a different set of gcc/qemu-sytem-aarch64/gdb.

    minimal-aarch64.c:

    #include <stdint.h>
    
    uint64_t raw_read_gic_gicc_bpr1_el1(void) {
        uint64_t gicc_bpr1_el1 = 0;
        __asm__ __volatile__("mrs %0, s3_0_c12_c12_3\n\t" : "=r" (gicc_bpr1_el1) :  : "memory");
        return gicc_bpr1_el1; 
    }
    
    void set_gic_gicc_bpr1_el1(uint64_t value)
    {
        __asm__ __volatile__("msr s3_0_c12_c12_3, %0\n\t" : : "r" (value) : "memory");
    }
    
    int main(int argc, char** argv)
    {
        uint64_t state = 0;
    
        set_gic_gicc_bpr1_el1(0x00);
        state = raw_read_gic_gicc_bpr1_el1();   
        
        set_gic_gicc_bpr1_el1(0x07);
        state = raw_read_gic_gicc_bpr1_el1();   
    }
    

    Compiling:

    /opt/arm/10/gcc-arm-10.2-2020.11-x86_64-aarch64-none-elf/bin/aarch64-none-elf-gcc -g --specs=rdimon.specs -o minimal-aarch64.elf minimal-aarch64.c
    

    Starting QEMU:

    /opt/qemu-5.2.0/bin/qemu-system-aarch64 -semihosting -m 1M -nographic -serial telnet::4444,server,nowait -machine virt,gic-version=3,secure=on,virtualization=off -S -gdb tcp::1234,ipv4 -cpu cortex-a72 -kernel minimal-aarch64.elf
    

    Starting GDB:

    /opt/gdb/gdb-10.1-aarch64-elf-x86_64-linux-gnu/bin/aarch64-elf-gdb -tui --quiet -nx -ex 'target remote localhost:1234' -ex 'load' --ex 'b main'  minimal-aarch64.elf
    

    GDG session:

    remote Thread 1.1 In: main                                                                                                                                                                                                                                  L23   PC: 0x400360 
    Loading section .text, size 0x3894 lma 0x400040
    Loading section .fini, size 0x34 lma 0x4038d4
    Loading section .rodata, size 0x74 lma 0x403908
    Loading section .eh_frame, size 0x4 lma 0x40397c
    Loading section .init_array, size 0x8 lma 0x413980
    Loading section .fini_array, size 0x8 lma 0x413988
    Loading section .data, size 0x10c8 lma 0x413990
    Start address 0x0000000000400178, load size 19020
    Transfer rate: 1326 KB/sec, 1118 bytes/write.
    Breakpoint 1 at 0x400338: file minimal-aarch64.c, line 16.
    (gdb) c
    Continuing.
    
    Breakpoint 1, main (argc=1, argv=0x400fffe0) at minimal-aarch64.c:16
    (gdb) p/x state
    $1 = 0x0
    (gdb) step
    set_gic_gicc_bpr1_el1 (value=0) at minimal-aarch64.c:11
    main (argc=1, argv=0x400fffe0) at minimal-aarch64.c:19
    (gdb) p/x state
    $2 = 0x0
    (gdb) step
    raw_read_gic_gicc_bpr1_el1 () at minimal-aarch64.c:4
    main (argc=1, argv=0x400fffe0) at minimal-aarch64.c:21
    set_gic_gicc_bpr1_el1 (value=7) at minimal-aarch64.c:11
    main (argc=1, argv=0x400fffe0) at minimal-aarch64.c:22
    raw_read_gic_gicc_bpr1_el1 () at minimal-aarch64.c:4
    main (argc=1, argv=0x400fffe0) at minimal-aarch64.c:23
    (gdb) p/x state
    $3 = 0x7
    (gdb) 
    

    Please note that some values may be invalid, at least according to the Arm Cortex-A78C Core Technical Reference Manual Revision r0p1 (I could not find the information in the A72 documentation):

    The minimum value implemented of ICC_BPR1_EL1 Secure register is 0x2.
    
    The minimum value implemented of ICC_BPR1_EL1 Non-secure register is 0x3.
    

    But according to the Arm Architecture System Registers document version 2020-12:

    enter image description here

    This may explain why you cannot write a 0 value in ICC-BPR1-EL1.