* where/how arm start first jump from svc to user in kernel
@ 2014-10-15 3:37 vichy
2014-10-15 16:20 ` Catalin Marinas
0 siblings, 1 reply; 11+ messages in thread
From: vichy @ 2014-10-15 3:37 UTC (permalink / raw)
To: linux-arm-kernel
hi all:
When tracing arm kernel source code, I cannot find the place where and
how kernel first time jump from svc mode to user mode to execute the
first user mode program.
Would you mind to show me where/how it happen?
appreciate your kind help,
^ permalink raw reply [flat|nested] 11+ messages in thread
* where/how arm start first jump from svc to user in kernel
2014-10-15 3:37 where/how arm start first jump from svc to user in kernel vichy
@ 2014-10-15 16:20 ` Catalin Marinas
2014-10-21 11:37 ` vichy
0 siblings, 1 reply; 11+ messages in thread
From: Catalin Marinas @ 2014-10-15 16:20 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, Oct 15, 2014 at 04:37:14AM +0100, vichy wrote:
> hi all:
> When tracing arm kernel source code, I cannot find the place where and
> how kernel first time jump from svc mode to user mode to execute the
> first user mode program.
>
> Would you mind to show me where/how it happen?
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.
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).
--
Catalin
^ permalink raw reply [flat|nested] 11+ messages in thread
* where/how arm start first jump from svc to user in kernel
2014-10-15 16:20 ` Catalin Marinas
@ 2014-10-21 11:37 ` vichy
2014-10-21 12:41 ` Mark Rutland
2014-10-21 22:00 ` Christopher Covington
0 siblings, 2 replies; 11+ messages in thread
From: vichy @ 2014-10-21 11:37 UTC (permalink / raw)
To: linux-arm-kernel
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.
Sincerely appreciate your kind help,
>
> --
> Catalin
^ permalink raw reply [flat|nested] 11+ messages in thread
* where/how arm start first jump from svc to user in kernel
2014-10-21 11:37 ` vichy
@ 2014-10-21 12:41 ` Mark Rutland
2014-10-21 13:49 ` vichy
2014-10-21 22:00 ` Christopher Covington
1 sibling, 1 reply; 11+ messages in thread
From: Mark Rutland @ 2014-10-21 12:41 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Oct 21, 2014 at 12:37:02PM +0100, 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?
Essentially yes, though the PC is not always the ELF entry address.
Consider returning from syscalls or interrupts.
> 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.
There is no movs pc, lr equivalent in AArch64. The eret instruction is
the only mechanism for dropping to a lower privileged exception level
(e.g. to EL0 userspace from an EL1 kernel).
The A32 eret instruction was only added with the ARMv7 virtualization
extensions (and in PL1 behaves as movs pc, lr if present). So it's
necessary to us movs pc, lr to function across a wide variety of CPUs,
and there's no benefit to using eret.
Thanks,
Mark.
^ permalink raw reply [flat|nested] 11+ messages in thread
* where/how arm start first jump from svc to user in kernel
2014-10-21 12:41 ` Mark Rutland
@ 2014-10-21 13:49 ` vichy
2014-10-21 16:15 ` Mark Rutland
0 siblings, 1 reply; 11+ messages in thread
From: vichy @ 2014-10-21 13:49 UTC (permalink / raw)
To: linux-arm-kernel
hi Mark:
2014-10-21 20:41 GMT+08:00 Mark Rutland <mark.rutland@arm.com>:
> There is no movs pc, lr equivalent in AArch64. The eret instruction is
> the only mechanism for dropping to a lower privileged exception level
> (e.g. to EL0 userspace from an EL1 kernel).
>
> The A32 eret instruction was only added with the ARMv7 virtualization
Per your explanation, EL0/EL1/EL3 of A32 are still using movs pc, lr, right?
> extensions (and in PL1 behaves as movs pc, lr if present). So it's
Is here a type? (PL1 should be EL1?)
> necessary to us movs pc, lr to function across a wide variety of CPUs,
> and there's no benefit to using eret.
thanks for your kind help,
^ permalink raw reply [flat|nested] 11+ messages in thread
* where/how arm start first jump from svc to user in kernel
2014-10-21 13:49 ` vichy
@ 2014-10-21 16:15 ` Mark Rutland
2014-10-27 13:32 ` vichy
0 siblings, 1 reply; 11+ messages in thread
From: Mark Rutland @ 2014-10-21 16:15 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
> 2014-10-21 20:41 GMT+08:00 Mark Rutland <mark.rutland@arm.com>:
> > There is no movs pc, lr equivalent in AArch64. The eret instruction is
> > the only mechanism for dropping to a lower privileged exception level
> > (e.g. to EL0 userspace from an EL1 kernel).
> >
> > The A32 eret instruction was only added with the ARMv7 virtualization
> Per your explanation, EL0/EL1/EL3 of A32 are still using movs pc, lr, right?
In Linux we use move pc, lr on ARMv7 for dropping from PL1 to PL0. If
that's run on ARMv8, it's no different.
On ARMv8, 32-bit software runnign at EL3 (which would be in PL1) could
use eret, or could use MOVS PC, LR.
> > extensions (and in PL1 behaves as movs pc, lr if present). So it's
> Is here a type? (PL1 should be EL1?)
Not a typo. While ARMv8 defines things in terms of Exception Levels,
ARMv7 defined things in terms of Privilege Levels, and this is carried
over to AArch32 in ARMv8.
The mapping of Privilege Levels to Exception Levels is dependent on
several factors. The ARMv8 ARM ARM describes the two in more detail in
"Execution privilege, Exception levels, and AArch32 Privilege levels".
Thanks,
Mark.
^ permalink raw reply [flat|nested] 11+ messages in thread
* where/how arm start first jump from svc to user in kernel
2014-10-21 11:37 ` vichy
2014-10-21 12:41 ` Mark Rutland
@ 2014-10-21 22:00 ` Christopher Covington
1 sibling, 0 replies; 11+ messages in thread
From: Christopher Covington @ 2014-10-21 22:00 UTC (permalink / raw)
To: linux-arm-kernel
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
^ permalink raw reply [flat|nested] 11+ messages in thread
* where/how arm start first jump from svc to user in kernel
2014-10-21 16:15 ` Mark Rutland
@ 2014-10-27 13:32 ` vichy
2014-10-27 14:25 ` Mark Rutland
0 siblings, 1 reply; 11+ messages in thread
From: vichy @ 2014-10-27 13:32 UTC (permalink / raw)
To: linux-arm-kernel
hi Mark and all:
> In Linux we use move pc, lr on ARMv7 for dropping from PL1 to PL0. If
> that's run on ARMv8, it's no different.
>
> On ARMv8, 32-bit software runnign at EL3 (which would be in PL1) could
> use eret, or could use MOVS PC, LR.
>From your explanation, it seems no matter EL3/EL2/EL1 in
ARMv8+aarch32, we both can use "eret" or "MOVS PC, LR" return back to
lower privilege level.
BTW, Could we get the conclusion that ARMv7 = ARMv8 +aarch32?
>
>> > extensions (and in PL1 behaves as movs pc, lr if present). So it's
>> Is here a type? (PL1 should be EL1?)
>
> Not a typo. While ARMv8 defines things in terms of Exception Levels,
> ARMv7 defined things in terms of Privilege Levels, and this is carried
> over to AArch32 in ARMv8.
>
> The mapping of Privilege Levels to Exception Levels is dependent on
> several factors. The ARMv8 ARM ARM describes the two in more detail in
> "Execution privilege, Exception levels, and AArch32 Privilege levels".
Below is excerpted from the section you mentioned.
System, FIQ, IRQ, Supervisor, Abort, and Undefined modes are implemented:
In Secure state If either:
? EL3 is using AArch32.
? EL3 is using AArch64 and EL1 is using AArch32.
How could we mixed AArch32/AArch64 in different Exception level?
in ARMv8, aarch32/aarch64 could switch each other without any
restriction by only setting up M[4], bit [4] in SPSR when return?
appreciate your kind help, ^^
^ permalink raw reply [flat|nested] 11+ messages in thread
* where/how arm start first jump from svc to user in kernel
2014-10-27 13:32 ` vichy
@ 2014-10-27 14:25 ` Mark Rutland
2014-10-27 17:41 ` vichy
0 siblings, 1 reply; 11+ messages in thread
From: Mark Rutland @ 2014-10-27 14:25 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Oct 27, 2014 at 01:32:52PM +0000, vichy wrote:
> hi Mark and all:
>
> > In Linux we use move pc, lr on ARMv7 for dropping from PL1 to PL0. If
> > that's run on ARMv8, it's no different.
> >
> > On ARMv8, 32-bit software runnign at EL3 (which would be in PL1) could
> > use eret, or could use MOVS PC, LR.
> From your explanation, it seems no matter EL3/EL2/EL1 in
> ARMv8+aarch32, we both can use "eret" or "MOVS PC, LR" return back to
> lower privilege level.
Generally, the mechanisms are the same as in ARMv7. In Hyp, ERET must be
used. In (most) modes other than Hyp, ERET behaves as MOVS PC, LR. While
you could use ERET in those cases, there's no benefit to doing so.
> BTW, Could we get the conclusion that ARMv7 = ARMv8 +aarch32?
AArch32 in ARMv8 is largely the same as ARMv7, but there are some
differences.
> >> > extensions (and in PL1 behaves as movs pc, lr if present). So it's
> >> Is here a type? (PL1 should be EL1?)
> >
> > Not a typo. While ARMv8 defines things in terms of Exception Levels,
> > ARMv7 defined things in terms of Privilege Levels, and this is carried
> > over to AArch32 in ARMv8.
> >
> > The mapping of Privilege Levels to Exception Levels is dependent on
> > several factors. The ARMv8 ARM ARM describes the two in more detail in
> > "Execution privilege, Exception levels, and AArch32 Privilege levels".
> Below is excerpted from the section you mentioned.
>
> System, FIQ, IRQ, Supervisor, Abort, and Undefined modes are implemented:
> In Secure state If either:
> ? EL3 is using AArch32.
> ? EL3 is using AArch64 and EL1 is using AArch32.
>
> How could we mixed AArch32/AArch64 in different Exception level?
> in ARMv8, aarch32/aarch64 could switch each other without any
> restriction by only setting up M[4], bit [4] in SPSR when return?
If you have A64 code in an exception level in AArch64, it can perform an
exception return to a lower exception level in AArch64 or AArch32 via
the A64 ERET instruction, after setting up the SPSR_ELx appropriately
(including, but not limited to M[4]).
If you have A32/T32 code in an exception level in AArch32, it cannot
perform an exception return to AArch64. For performing an exception
return to AArch32, some restrictions apply (e.g. from Hyp you must use
ERET), and for those you must consult the ARM ARM.
Thanks,
Mark.
^ permalink raw reply [flat|nested] 11+ messages in thread
* where/how arm start first jump from svc to user in kernel
2014-10-27 14:25 ` Mark Rutland
@ 2014-10-27 17:41 ` vichy
2014-10-29 10:43 ` Mark Rutland
0 siblings, 1 reply; 11+ messages in thread
From: vichy @ 2014-10-27 17:41 UTC (permalink / raw)
To: linux-arm-kernel
hi Mark:
>
>> BTW, Could we get the conclusion that ARMv7 = ARMv8 +aarch32?
>
> AArch32 in ARMv8 is largely the same as ARMv7, but there are some
> differences.
Would you mind to let me know where we can find the difference in spec
or some place else?
appreciate your kind help :)
^ permalink raw reply [flat|nested] 11+ messages in thread
* where/how arm start first jump from svc to user in kernel
2014-10-27 17:41 ` vichy
@ 2014-10-29 10:43 ` Mark Rutland
0 siblings, 0 replies; 11+ messages in thread
From: Mark Rutland @ 2014-10-29 10:43 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Oct 27, 2014 at 05:41:58PM +0000, vichy wrote:
> hi Mark:
> >
> >> BTW, Could we get the conclusion that ARMv7 = ARMv8 +aarch32?
> >
> > AArch32 in ARMv8 is largely the same as ARMv7, but there are some
> > differences.
> Would you mind to let me know where we can find the difference in spec
> or some place else?
You will need to read the ARMv8 architecture reference manual and
compare that to the ARMv7 architecture reference manual for the details
you are interested in.
You can find both on ARM infocenter [1] after registration.
Thanks,
Mark.
[1] http://infocenter.arm.com
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2014-10-29 10:43 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-10-15 3:37 where/how arm start first jump from svc to user in kernel vichy
2014-10-15 16:20 ` Catalin Marinas
2014-10-21 11:37 ` vichy
2014-10-21 12:41 ` Mark Rutland
2014-10-21 13:49 ` vichy
2014-10-21 16:15 ` Mark Rutland
2014-10-27 13:32 ` vichy
2014-10-27 14:25 ` Mark Rutland
2014-10-27 17:41 ` vichy
2014-10-29 10:43 ` Mark Rutland
2014-10-21 22:00 ` Christopher Covington
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).