where/how arm start first jump from svc to user in kernel
Christopher Covington
cov at codeaurora.org
Tue Oct 21 15:00:06 PDT 2014
On 10/21/2014 07:37 AM, vichy wrote:
> hi Catalin:
>> As a quick answer, it's ret_to_user (or ret_slow_syscall to be more
>> precise) in arch/arm/kernel/entry-common.S.
>>
>> How it gets there is a bit more complicated. The initial kernel call
>> path:
>>
>> start_kernel()
>> rest_init()
>> kernel_thread(kernel_init)
>>
>> kernel_thread() creates a new thread which executes the kernel_init()
>> function. After the multitude of calls that kernel_thread() -> do_fork()
>> does, it eventually calls copy_thread() which sets the
>> thread->cpu_context.pc to ret_from_fork and the actual address of
>> kernel_init in thread->cpu_context.r5. When the kernel eventually
>> switches to the new kernel thread, it will jump to
>> thread->cpu_context.pc which is ret_from_fork (see __switch_to in
>> arch/arm/kernel/entry-armv.S). ret_from_fork() branches to kernel_init()
>> but sets the return address (LR) to label 1 in ret_from_fork.
>>
>> After kernel_init() does its work, it eventually calls
>> run_init_process() which invokes do_execve() and eventually
>> load_elf_binary(). If the ELF binary was successfully loaded, this
>> function calls start_thread() with the ELF entry point. The
>> start_thread() function populates the pt_regs structure on the stack so
>> that regs->ARM_pc points to the ELF entry point and regs->ARM_cpsr has
>> the user mode bits set.
> Many thanks for your kind and detail explanation.
>
>>
>> Going back to kernel_init(), if run_init_process() was successful, it
>> returns to label 1 in ret_from_fork which branches to ret_slow_syscall
>> which eventually returns to user space via the restore_user_regs macro
>> (in arch/arm/kernel/entry-header.S).
>
> Below is excerpted from arch/arm/kernel/entry-header.S
>
> .macro restore_user_regs, fast = 0, offset = 0
> ldr r1, [sp, #\offset + S_PSR] @ get calling cpsr
> ldr lr, [sp, #\offset + S_PC]! @ get pc
> msr spsr_cxsf, r1 @ save in spsr_svc
> ..........
> add sp, sp, #S_FRAME_SIZE - S_PC
> movs pc, lr @ return & move
> spsr_svc into cpsr
> .endm
>
> so before armv8 architecture and kernel switch to user mode (arm svn-> user),
> we first put the cpsr, the mode we want to jump to, in current spsr.
> Then update pc counter to where the ELF entry, right?
>
> BTW, in armv8, aarch64, arch/arm64/entry.S, kernel_exit,
> (if I look at the right place.)
>
> .macro kernel_exit, el, ret = 0
> ..............................
> msr elr_el1, x21 // set up the return data
> msr spsr_el1, x22
> .if \el == 0
> msr sp_el0, x23
> .endif
> pop x10, x11
> ............................
> ldr lr, [sp], #S_FRAME_SIZE - S_LR // load LR and restore SP
> eret // return to kernel
> .endm
>
> Is there any special reason we use eret in v8 instead of modify pc in v7?
> I have looked arm v8 architecture reference menu but get no explanation so far.
"In AArch64 state, the PC is not a general purpose register and you cannot
access it explicitly."
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0801a/BABGHBJC.html
Chris
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
More information about the linux-arm-kernel
mailing list