1/*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#include <config.h>
8#include <model/statedata.h>
9#include <arch/fastpath/fastpath.h>
10#include <arch/kernel/traps.h>
11#include <arch/machine/debug.h>
12#include <arch/machine/debug_conf.h>
13#include <api/syscall.h>
14#include <linker.h>
15#include <machine/fpu.h>
16
17#include <benchmark/benchmark_track.h>
18#include <benchmark/benchmark_utilisation.h>
19
20/** DONT_TRANSLATE */
21void VISIBLE NORETURN restore_user_context(void)
22{
23    NODE_UNLOCK_IF_HELD;
24
25    word_t cur_thread_reg = (word_t) NODE_STATE(ksCurThread);
26
27    c_exit_hook();
28
29#ifdef ARM_CP14_SAVE_AND_RESTORE_NATIVE_THREADS
30    restore_user_debug_context(NODE_STATE(ksCurThread));
31#endif
32
33#ifdef CONFIG_HAVE_FPU
34    lazyFPURestore(NODE_STATE(ksCurThread));
35#endif /* CONFIG_HAVE_FPU */
36
37    if (config_set(CONFIG_ARM_HYPERVISOR_SUPPORT)) {
38        asm volatile(
39            /* Set stack pointer to point at the r0 of the user context. */
40            "mov sp, %[cur_thread_reg] \n"
41            /* Pop user registers */
42            "pop {r0-r12}              \n"
43            /* Retore the user stack pointer */
44            "pop {lr}                  \n"
45            "msr sp_usr, lr            \n"
46            /* prepare the exception return lr */
47            "ldr lr, [sp, #4]          \n"
48            "msr elr_hyp, lr           \n"
49            /* prepare the user status register */
50            "ldr lr, [sp, #8]          \n"
51            "msr spsr_hyp, lr          \n"
52            /* Finally, pop our LR */
53            "pop {lr}                  \n"
54            /* Return to user */
55            "eret"
56            : /* no output */
57            : [cur_thread_reg] "r"(cur_thread_reg)
58            : "memory"
59        );
60    } else {
61        asm volatile("mov sp, %[cur_thread] \n\
62                  ldmdb sp, {r0-lr}^ \n\
63                  rfeia sp"
64                     : /* no output */
65                     : [cur_thread] "r"(cur_thread_reg + NextIP * sizeof(word_t))
66                    );
67    }
68    UNREACHABLE();
69}
70