1/*
2 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#include <object.h>
8#include <machine.h>
9#include <arch/model/statedata.h>
10#include <arch/kernel/vspace.h>
11#include <arch/kernel/thread.h>
12#include <linker.h>
13
14void Arch_switchToThread(tcb_t *tcb)
15{
16    /* set PD */
17    setVMRoot(tcb);
18#ifdef ENABLE_SMP_SUPPORT
19    asm volatile("movq %[value], %%gs:%c[offset]"
20                 :
21                 : [value] "r"(&tcb->tcbArch.tcbContext.registers[Error + 1]),
22                 [offset] "i"(OFFSETOF(nodeInfo_t, currentThreadUserContext)));
23#endif
24    if (config_set(CONFIG_KERNEL_X86_IBPB_ON_CONTEXT_SWITCH)) {
25        x86_ibpb();
26    }
27
28    if (config_set(CONFIG_KERNEL_X86_RSB_ON_CONTEXT_SWITCH)) {
29        x86_flush_rsb();
30    }
31}
32
33BOOT_CODE void Arch_configureIdleThread(tcb_t *tcb)
34{
35    setRegister(tcb, FLAGS, FLAGS_USER_DEFAULT);
36    setRegister(tcb, NextIP, (uint64_t)idleThreadStart);
37    setRegister(tcb, CS, SEL_CS_0);
38    setRegister(tcb, SS, SEL_DS_0);
39    /* We set the RSP to 0, even though the idle thread will never touch it, as it
40     * allows us to distinguish an interrupt from the idle thread from an interrupt
41     * from kernel execution, just by examining the saved RSP value (since the kernel
42     * thread will have a valid RSP, and never 0). See traps.S for the other side of this
43     */
44    setRegister(tcb, RSP, 0);
45}
46
47void Arch_switchToIdleThread(void)
48{
49    tcb_t *tcb = NODE_STATE(ksIdleThread);
50    /* Force the idle thread to run on kernel page table */
51    setVMRoot(tcb);
52#ifdef ENABLE_SMP_SUPPORT
53    asm volatile("movq %[value], %%gs:%c[offset]"
54                 :
55                 : [value] "r"(&tcb->tcbArch.tcbContext.registers[Error + 1]),
56                 [offset] "i"(OFFSETOF(nodeInfo_t, currentThreadUserContext)));
57#endif
58}
59
60void Arch_activateIdleThread(tcb_t *tcb)
61{
62    /* Don't need to do anything */
63}
64
65void Mode_postModifyRegisters(tcb_t *tptr)
66{
67    /* Setting Error to 0 will force a return by the interrupt path, which
68     * does a full restore. Unless we're the current thread, in which case
69     * we still have to go back out via a sysret */
70    if (tptr != NODE_STATE(ksCurThread)) {
71        setRegister(tptr, Error, 0);
72    }
73}
74