using arm LDM instruction to transfer data into user mode registers

2

I'm trying to restore user mode registers from svc mode on a cortex-a5 chip by using LDM instruction. I referred a technical guide for instruction LDM, the syntax is:

LDM{addr_mode}{cond} Rn{!},reglist{^}

'^' if specified (in a mode other than User or System) means one of two possible special actions will be taken:

  • Data is transferred into the User mode registers instead of the current mode registers (in the case where Reglist does not contain the PC).
  • If Reglist does contain the PC, the normal multiple register transfer happens and the SPSR is copied into the CPSR. This is used for returning from exception handlers.

Here is the assembly code:

// the CPU is in SVC mode, and is to change to user mode
mrs      r0,   spsr    // copy spsr/cpsr to r0/r1
mrs      r1,   cpsr
add      lr, sp, #8    // saves user mode stack to lr
push     {lr}
mov      r5, sp
ldmia    r5, {r7, r8}  // compare {r7,r8} with {sp,lr}
ldmia    r5! {sp, lr}^
ldr      r3, =0x0      // to generate exception
movs     pc, r3        // return to user mode

after the code is executed, pre-fetch abort exception occurs, below is the register info:

r0:0x00000010 r1:0x600000d3  r2:0x02020202  r3:0x03030303
r4:0x03030303 r5:0x800bca2c  r6:0x06060606  r7:0x800bca38
r8:0x80018d54 r9:0x09090909 r10:0x10101010 r11:0x11111111
ip:0x12121212 sp:0x800bc964  lr:0x800bca38  pc:0x00000000
CPSR:0x00000010

The value of r1 is 0x600000d3, that means previously the CPU is in SVC mode, the value of r0 is 0x00000010, so after "movs pc, r3" is executed, the CPU will go to user mode. The value of CPSR[4:0] is 0x10, it means CPU is running at user mode, so the CPU mode is changed successfully. The value of {r7, r8} is right, however, {sp, lr} is wrong.

Can anyone explain what's wrong with my code? Thanks in advance.

arm
cortex-a
usermode
asked on Stack Overflow Jul 14, 2018 by user3197437 • edited Jul 23, 2018 by artless noise

1 Answer

0

SVC mode has a separate banked register for SP and LR. Your code is saving the SVC (or pre-fetch mode) SP and LR which are not the original user mode registers. There is no need for these lines,

add      lr, sp, #8    // saves user mode stack to lr
push     {lr}

...

ldmia    r5! {sp, lr}^

You only need the ldmia r5! {sp, lr}^ if you are doing a context switch or you have some sort of non-linear functionality like a signal. movs pc,lr is probably more appropriate for a return from SVC mode.

See also:
             ARM banked registers
             Accessing banked registers

answered on Stack Overflow Jul 20, 2018 by artless noise • edited Jul 20, 2018 by artless noise

User contributions licensed under CC BY-SA 3.0