Why the arm port of Linux kernel did not backup the "cpsr" register during the context switch?

385 Views Asked by At

The switch to assembly of arm32 in linux is like that: you coud see there are no cpsr reigister backup, compare to others arch, like mips or riscv, which corresponding mstatus and status register are all do the backup and restore during the _switch_to, so, why there are the differences?

  12948 8010d328 <__switch_to>:
  12949 8010d328:       e281c018        add     ip, r1, #24
  12950 8010d32c:       e8ac6ff0        stmia   ip!, {r4, r5, r6, r7, r8, r9, sl, fp, sp, lr}
  12951 8010d330:       e592405c        ldr     r4, [r2, #92]   ; 0x5c
  12952 8010d334:       e5925060        ldr     r5, [r2, #96]   ; 0x60
  12953 8010d338:       ee1d7f50        mrc     15, 0, r7, cr13, cr0, {2}
  12954 8010d33c:       ee0d4f70        mcr     15, 0, r4, cr13, cr0, {3}
  12955 8010d340:       ee0d5f50        mcr     15, 0, r5, cr13, cr0, {2}
  12956 8010d344:       e5817060        str     r7, [r1, #96]   ; 0x60
  12957 8010d348:       e1a05000        mov     r5, r0
  12958 8010d34c:       e2824018        add     r4, r2, #24
  12959 8010d350:       e59f000c        ldr     r0, [pc, #12]   ; 8010d364 <__switch_to+0x3c>
  12960 8010d354:       e3a01002        mov     r1, #2
  12961 8010d358:       eb00c813        bl      8013f3ac <atomic_notifier_call_chain>
  12962 8010d35c:       e1a00005        mov     r0, r5
  12963 8010d360:       e894aff0        ldm     r4, {r4, r5, r6, r7, r8, r9, sl, fp, sp, pc}
  12964 8010d364:       80b61200        .word   0x80b61200
  12965 8010d368:       e58d403c        str     r4, [sp, #60]   ; 0x3c
  12966 8010d36c:       e1a0f009        mov     pc, r9
1

There are 1 best solutions below

5
artless noise On

There are a few different uses of cpsr and it is not clear which one you are referring to.

  1. The supervisor mode cpsr.
  2. The user mode cpsr.
  3. Some exception cpsr.

These values are stored during an exception on an ARM cpu.

See: Linux kernel ARM exception stack

This makes all 'spsr' except the svc and user mode meaningless.

Note: The ARM has banked SP, cpsr and lr. Other referenced architectures may or may not have banked registers. For certain, all need to restore the user mode status register or user CPSR on ARM.

The switch_to assembler is used to switch kernel state for a process. The sp that is restored is an 8k page where the lower portion is a thread_info holding a copy of the user registers. The switch_to() function takes a from and to thread_info held in R1 and R2.

The code,

  add     r4, r2, #24
  ldm     r4, {r4, r5, r6, r7, r8, r9, sl, fp, sp, pc}

Is more like a longjmp() to a previous call to switch_to(); the code is not heading to the same place it was called from necessarily. The SPSR which is the return to user mode CPSR (and lr). These are saved in r1,r2 by the vector stubs. They are restored by the restore_user_regs macro. The instruction movs pc, lr will restore the user CPSR and return from an exception/syscall.

The cpsr (SVC) active with kernel code is protected by the 'C' ABI which says the status bits are not valid after a function call. It is also assumed the any svc code uses the same 'T' bit (all kernel code is either Thumb or ARM only) and the function needs to be called with 'F' and 'I' bits in a known state. switch_to callers should use the stacking mask functions OR never have interrupts masked. I think it is probably the last one. If you try to mix Thumb/ARM code or use/change interrupt masking with calls to switch_to, it would be an issue; otherwise, there is no need to save/restore this value.