Skip to content

Commit e130338

Browse files
mrutland-armctmarinas
authored andcommitted
arm64: entry: call exit_to_user_mode() from C
When handling an exception from EL0, we perform the entry work in that exception's C handler, and once the C handler has finished, we return back to the entry assembly. Subsequently in the common `ret_to_user` assembly we perform the exit work that balances with the entry work. This can be somewhat difficult to follow, and makes it hard to rework the return paths (e.g. to pass additional context to the exit code, or to have exception return logic for specific exceptions). This patch reworks the entry code such that each EL0 C exception handler is responsible for both the entry and exit work. This clearly balances the two (and will permit additional variation in future), and avoids an unnecessary bounce between assembly and C in the common case, leaving `ret_from_fork` as the only place assembly has to call the exit code. This means that the exit work is now inlined into the C handler, which is already the case for the entry work, and allows the compiler to generate better code (e.g. by immediately returning when there is no exit work to perform). To align with other exception entry/exit helpers, enter_from_user_mode() is updated to take the EL0 pt_regs as a parameter, though this is currently unused. There should be no functional change as a result of this patch. However, this should lead to slightly better backtraces when an error is encountered within do_notify_resume(), as the C handler should appear in the backtrace, indicating the specific exception that the kernel was entered with. Signed-off-by: Mark Rutland <[email protected]> Cc: James Morse <[email protected]> Cc: Joey Gouly <[email protected]> Cc: Marc Zyngier <[email protected]> Cc: Will Deacon <[email protected]> Reviewed-by: Joey Gouly <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Catalin Marinas <[email protected]>
1 parent 4d1c2ee commit e130338

File tree

2 files changed

+48
-30
lines changed

2 files changed

+48
-30
lines changed

arch/arm64/kernel/entry-common.c

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ static __always_inline void __enter_from_user_mode(void)
104104
trace_hardirqs_off_finish();
105105
}
106106

107-
static __always_inline void enter_from_user_mode(void)
107+
static __always_inline void enter_from_user_mode(struct pt_regs *regs)
108108
{
109109
__enter_from_user_mode();
110110
}
@@ -116,19 +116,12 @@ static __always_inline void enter_from_user_mode(void)
116116
*/
117117
static __always_inline void __exit_to_user_mode(void)
118118
{
119-
120119
trace_hardirqs_on_prepare();
121120
lockdep_hardirqs_on_prepare(CALLER_ADDR0);
122121
user_enter_irqoff();
123122
lockdep_hardirqs_on(CALLER_ADDR0);
124123
}
125124

126-
static __always_inline void exit_to_user_mode(void)
127-
{
128-
mte_check_tfsr_exit();
129-
__exit_to_user_mode();
130-
}
131-
132125
static __always_inline void prepare_exit_to_user_mode(struct pt_regs *regs)
133126
{
134127
unsigned long flags;
@@ -140,10 +133,16 @@ static __always_inline void prepare_exit_to_user_mode(struct pt_regs *regs)
140133
do_notify_resume(regs, flags);
141134
}
142135

143-
asmlinkage void noinstr asm_exit_to_user_mode(struct pt_regs *regs)
136+
static __always_inline void exit_to_user_mode(struct pt_regs *regs)
144137
{
145138
prepare_exit_to_user_mode(regs);
146-
exit_to_user_mode();
139+
mte_check_tfsr_exit();
140+
__exit_to_user_mode();
141+
}
142+
143+
asmlinkage void noinstr asm_exit_to_user_mode(struct pt_regs *regs)
144+
{
145+
exit_to_user_mode(regs);
147146
}
148147

149148
/*
@@ -477,9 +476,10 @@ static void noinstr el0_da(struct pt_regs *regs, unsigned long esr)
477476
{
478477
unsigned long far = read_sysreg(far_el1);
479478

480-
enter_from_user_mode();
479+
enter_from_user_mode(regs);
481480
local_daif_restore(DAIF_PROCCTX);
482481
do_mem_abort(far, esr, regs);
482+
exit_to_user_mode(regs);
483483
}
484484

485485
static void noinstr el0_ia(struct pt_regs *regs, unsigned long esr)
@@ -494,37 +494,42 @@ static void noinstr el0_ia(struct pt_regs *regs, unsigned long esr)
494494
if (!is_ttbr0_addr(far))
495495
arm64_apply_bp_hardening();
496496

497-
enter_from_user_mode();
497+
enter_from_user_mode(regs);
498498
local_daif_restore(DAIF_PROCCTX);
499499
do_mem_abort(far, esr, regs);
500+
exit_to_user_mode(regs);
500501
}
501502

502503
static void noinstr el0_fpsimd_acc(struct pt_regs *regs, unsigned long esr)
503504
{
504-
enter_from_user_mode();
505+
enter_from_user_mode(regs);
505506
local_daif_restore(DAIF_PROCCTX);
506507
do_fpsimd_acc(esr, regs);
508+
exit_to_user_mode(regs);
507509
}
508510

509511
static void noinstr el0_sve_acc(struct pt_regs *regs, unsigned long esr)
510512
{
511-
enter_from_user_mode();
513+
enter_from_user_mode(regs);
512514
local_daif_restore(DAIF_PROCCTX);
513515
do_sve_acc(esr, regs);
516+
exit_to_user_mode(regs);
514517
}
515518

516519
static void noinstr el0_fpsimd_exc(struct pt_regs *regs, unsigned long esr)
517520
{
518-
enter_from_user_mode();
521+
enter_from_user_mode(regs);
519522
local_daif_restore(DAIF_PROCCTX);
520523
do_fpsimd_exc(esr, regs);
524+
exit_to_user_mode(regs);
521525
}
522526

523527
static void noinstr el0_sys(struct pt_regs *regs, unsigned long esr)
524528
{
525-
enter_from_user_mode();
529+
enter_from_user_mode(regs);
526530
local_daif_restore(DAIF_PROCCTX);
527531
do_sysinstr(esr, regs);
532+
exit_to_user_mode(regs);
528533
}
529534

530535
static void noinstr el0_pc(struct pt_regs *regs, unsigned long esr)
@@ -534,61 +539,69 @@ static void noinstr el0_pc(struct pt_regs *regs, unsigned long esr)
534539
if (!is_ttbr0_addr(instruction_pointer(regs)))
535540
arm64_apply_bp_hardening();
536541

537-
enter_from_user_mode();
542+
enter_from_user_mode(regs);
538543
local_daif_restore(DAIF_PROCCTX);
539544
do_sp_pc_abort(far, esr, regs);
545+
exit_to_user_mode(regs);
540546
}
541547

542548
static void noinstr el0_sp(struct pt_regs *regs, unsigned long esr)
543549
{
544-
enter_from_user_mode();
550+
enter_from_user_mode(regs);
545551
local_daif_restore(DAIF_PROCCTX);
546552
do_sp_pc_abort(regs->sp, esr, regs);
553+
exit_to_user_mode(regs);
547554
}
548555

549556
static void noinstr el0_undef(struct pt_regs *regs)
550557
{
551-
enter_from_user_mode();
558+
enter_from_user_mode(regs);
552559
local_daif_restore(DAIF_PROCCTX);
553560
do_undefinstr(regs);
561+
exit_to_user_mode(regs);
554562
}
555563

556564
static void noinstr el0_bti(struct pt_regs *regs)
557565
{
558-
enter_from_user_mode();
566+
enter_from_user_mode(regs);
559567
local_daif_restore(DAIF_PROCCTX);
560568
do_bti(regs);
569+
exit_to_user_mode(regs);
561570
}
562571

563572
static void noinstr el0_inv(struct pt_regs *regs, unsigned long esr)
564573
{
565-
enter_from_user_mode();
574+
enter_from_user_mode(regs);
566575
local_daif_restore(DAIF_PROCCTX);
567576
bad_el0_sync(regs, 0, esr);
577+
exit_to_user_mode(regs);
568578
}
569579

570580
static void noinstr el0_dbg(struct pt_regs *regs, unsigned long esr)
571581
{
572582
/* Only watchpoints write FAR_EL1, otherwise its UNKNOWN */
573583
unsigned long far = read_sysreg(far_el1);
574584

575-
enter_from_user_mode();
585+
enter_from_user_mode(regs);
576586
do_debug_exception(far, esr, regs);
577587
local_daif_restore(DAIF_PROCCTX);
588+
exit_to_user_mode(regs);
578589
}
579590

580591
static void noinstr el0_svc(struct pt_regs *regs)
581592
{
582-
enter_from_user_mode();
593+
enter_from_user_mode(regs);
583594
cortex_a76_erratum_1463225_svc_handler();
584595
do_el0_svc(regs);
596+
exit_to_user_mode(regs);
585597
}
586598

587599
static void noinstr el0_fpac(struct pt_regs *regs, unsigned long esr)
588600
{
589-
enter_from_user_mode();
601+
enter_from_user_mode(regs);
590602
local_daif_restore(DAIF_PROCCTX);
591603
do_ptrauth_fault(regs, esr);
604+
exit_to_user_mode(regs);
592605
}
593606

594607
asmlinkage void noinstr el0t_64_sync_handler(struct pt_regs *regs)
@@ -647,14 +660,16 @@ asmlinkage void noinstr el0t_64_sync_handler(struct pt_regs *regs)
647660
static void noinstr el0_interrupt(struct pt_regs *regs,
648661
void (*handler)(struct pt_regs *))
649662
{
650-
enter_from_user_mode();
663+
enter_from_user_mode(regs);
651664

652665
write_sysreg(DAIF_PROCCTX_NOIRQ, daif);
653666

654667
if (regs->pc & BIT(55))
655668
arm64_apply_bp_hardening();
656669

657670
do_interrupt_handler(regs, handler);
671+
672+
exit_to_user_mode(regs);
658673
}
659674

660675
static void noinstr __el0_irq_handler_common(struct pt_regs *regs)
@@ -681,12 +696,13 @@ static void noinstr __el0_error_handler_common(struct pt_regs *regs)
681696
{
682697
unsigned long esr = read_sysreg(esr_el1);
683698

684-
enter_from_user_mode();
699+
enter_from_user_mode(regs);
685700
local_daif_restore(DAIF_ERRCTX);
686701
arm64_enter_nmi(regs);
687702
do_serror(regs, esr);
688703
arm64_exit_nmi(regs);
689704
local_daif_restore(DAIF_PROCCTX);
705+
exit_to_user_mode(regs);
690706
}
691707

692708
asmlinkage void noinstr el0t_64_error_handler(struct pt_regs *regs)
@@ -697,16 +713,18 @@ asmlinkage void noinstr el0t_64_error_handler(struct pt_regs *regs)
697713
#ifdef CONFIG_COMPAT
698714
static void noinstr el0_cp15(struct pt_regs *regs, unsigned long esr)
699715
{
700-
enter_from_user_mode();
716+
enter_from_user_mode(regs);
701717
local_daif_restore(DAIF_PROCCTX);
702718
do_cp15instr(esr, regs);
719+
exit_to_user_mode(regs);
703720
}
704721

705722
static void noinstr el0_svc_compat(struct pt_regs *regs)
706723
{
707-
enter_from_user_mode();
724+
enter_from_user_mode(regs);
708725
cortex_a76_erratum_1463225_svc_handler();
709726
do_el0_svc_compat(regs);
727+
exit_to_user_mode(regs);
710728
}
711729

712730
asmlinkage void noinstr el0t_32_sync_handler(struct pt_regs *regs)

arch/arm64/kernel/entry.S

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -564,8 +564,6 @@ SYM_CODE_START_LOCAL(ret_to_kernel)
564564
SYM_CODE_END(ret_to_kernel)
565565

566566
SYM_CODE_START_LOCAL(ret_to_user)
567-
mov x0, sp
568-
bl asm_exit_to_user_mode
569567
/* Ignore asynchronous tag check faults in the uaccess routines */
570568
clear_mte_async_tcf
571569
ldr x19, [tsk, #TSK_TI_FLAGS] // re-check for single-step
@@ -739,6 +737,8 @@ SYM_CODE_START(ret_from_fork)
739737
mov x0, x20
740738
blr x19
741739
1: get_current_task tsk
740+
mov x0, sp
741+
bl asm_exit_to_user_mode
742742
b ret_to_user
743743
SYM_CODE_END(ret_from_fork)
744744
NOKPROBE(ret_from_fork)

0 commit comments

Comments
 (0)