assemblyarm64armv8cortex-a

updating SCTLR_EL1 register not working in bare-matel ARMv8 program on QEMU


I am learning armv8 assembly program for my project. I wrote a simple program which tries to modify value of SCTLR_EL1 register. Unfortunately this code is not working. I am enabling I-cache, but the only thing I see is 0x0 in SCTLR_EL1.

.section .text
.global _start

_start:
    mov x0, xzr                 // Initialize x0 to zero
    // Set SCTLR_EL1 and confirm the modification
    mrs x0, SCTLR_EL1            // Read SCTLR_EL1 into x0
    orr x0, x0, #(1 << 12)       // Enable I-Cache
    msr SCTLR_EL1, x0            // Write back to SCTLR_EL1

    mrs x0, SCTLR_EL1            // Read the modified SCTLR_EL1 into x0
    // Store the value in a known memory location for debugging
    ldr x1, =sctlr_el1_val
    str x0, [x1]

    b loop

loop:
    b loop

.data
.align 8
sctlr_el1_val: .quad 0x0100        // Memory location to store the SCTLR_EL1 value

memmap

MEMORY
{
    rom  : ORIGIN = 0x00010000, LENGTH = 32K
}

SECTIONS
{
   .text : { *(.text*) } > rom
}

QEMU command used is as follows:

qemu-system-aarch64 -M virt -cpu cortex-a57 -m 512M -nographic -machine secure=on \
-S -s -kernel path_to_your_binary.elf -d in_asm,cpu

Output I see in console is:

PSTATE=000003cd ---- EL3h
 PC=0000000000000200 X00=0000000000000000 X01=0000000000000000
X02=0000000000000000 X03=0000000000000000 X04=0000000000000000
X05=0000000000000000 X06=0000000000000000 X07=0000000000000000
X08=0000000000000000 X09=0000000000000000 X10=0000000000000000
X11=0000000000000000 X12=0000000000000000 X13=0000000000000000
X14=0000000000000000 X15=0000000000000000 X16=0000000000000000
X17=0000000000000000 X18=0000000000000000 X19=0000000000000000
X20=0000000000000000 X21=0000000000000000 X22=0000000000000000
X23=0000000000000000 X24=0000000000000000 X25=0000000000000000
X26=0000000000000000 X27=0000000000000000 X28=0000000000000000
X29=0000000000000000 X30=0000000000000000  SP=0000000000000000
PSTATE=000003cd ---- EL3h

GDB did not show anything:

(gdb) x/x 0x0100
0x100:  0x00000000
(gdb) x/x $SCTLR_EL1
Value can't be converted to integer.
(gdb) p/x $SCTLR_EL1
$1 = void
(gdb) display  $SCTLR_EL1
1: $SCTLR_EL1 = void
(gdb)  p/x $CurrentEL
$2 = void

How can i modify CurrentEL and SCTLR_EL1? and how can I see those register value using GDB? Can someone please help? Thank you.


Solution

  • The QEMU output what you see is caused from an exception. As @Jester mentioned this is because you are enabling -machine secure=on. This option enables the ARM TrustZone extension. Use a higher program entry point address. An entry point at 0x40000000 should work.

    You also have forgotten the isb instruction after writing to SCTLR_EL1. When you read directly SCTLR_EL1 after the write-back without isb instruction, the content from SCTLR_EL1 is the old one (content before msr SCTLR_EL1, x0).

    .section .text
    .func _start,_start
    .type _start,%function
    .global _start
    
    _start:
        mov x0, xzr                 /* Initialize x0 to zero */
        mrs x0, SCTLR_EL1           /* Read SCTLR_EL1 into x0 */
        orr x0, x0, #(1 << 12)      /* Enable I-Cache */
        msr SCTLR_EL1, x0           /* Write back to SCTLR_EL1 */
        isb                         /* Instruction Synchronization barrier */
    
    loop:
        b loop
    
    .size _start, . - _start
    .endfunc
    

    To display the registers values with GDB you can use the command info registers or info all-registers. These should also include the SCTLR register.

    CurrentEL can be extracted from the current program status register (cpsr) in the output from GDB.

    For changing/switching to a lower exception levels there you can find enough samples here on SO on other sites.