summaryrefslogtreecommitdiff
path: root/Kernel/Arch/x86
diff options
context:
space:
mode:
authorGunnar Beutner <gbeutner@serenityos.org>2021-06-27 19:49:19 +0200
committerAndreas Kling <kling@serenityos.org>2021-06-28 15:55:00 +0200
commit32840dfa17494f15f9a77ecde9e2cfa8f198dcff (patch)
tree9ba7a73870178a32aa15c834705d4c7cf1a469be /Kernel/Arch/x86
parent076692b1efcac974eb6bd621ec1c950424398c1d (diff)
downloadserenity-32840dfa17494f15f9a77ecde9e2cfa8f198dcff.zip
Kernel: Implement more x86_64 context switching functionality
Diffstat (limited to 'Kernel/Arch/x86')
-rw-r--r--Kernel/Arch/x86/common/Processor.cpp71
-rw-r--r--Kernel/Arch/x86/i386/CPU.cpp82
-rw-r--r--Kernel/Arch/x86/i386/Processor.cpp3
-rw-r--r--Kernel/Arch/x86/x86_64/CPU.cpp37
-rw-r--r--Kernel/Arch/x86/x86_64/Processor.cpp71
5 files changed, 137 insertions, 127 deletions
diff --git a/Kernel/Arch/x86/common/Processor.cpp b/Kernel/Arch/x86/common/Processor.cpp
index a4d3af0cfb..48b9bbef7b 100644
--- a/Kernel/Arch/x86/common/Processor.cpp
+++ b/Kernel/Arch/x86/common/Processor.cpp
@@ -40,9 +40,11 @@ Atomic<u32> Processor::s_idle_cpu_mask { 0 };
extern "C" void thread_context_first_enter(void);
extern "C" void exit_kernel_thread(void);
-// The compiler can't see the calls to this function inside assembly.
-// Declare it, to avoid dead code warnings.
+// The compiler can't see the calls to these functions inside assembly.
+// Declare them, to avoid dead code warnings.
extern "C" void context_first_init(Thread* from_thread, Thread* to_thread, TrapFrame* trap) __attribute__((used));
+extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread) __attribute__((used));
+extern "C" u32 do_init_context(Thread* thread, u32 flags) __attribute__((used));
UNMAP_AFTER_INIT static void sse_init()
{
@@ -1165,4 +1167,69 @@ extern "C" void context_first_init([[maybe_unused]] Thread* from_thread, [[maybe
Scheduler::leave_on_first_switch(flags & ~0x200);
}
+extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread)
+{
+ VERIFY(from_thread == to_thread || from_thread->state() != Thread::Running);
+ VERIFY(to_thread->state() == Thread::Running);
+
+ bool has_fxsr = Processor::current().has_feature(CPUFeature::FXSR);
+ Processor::set_current_thread(*to_thread);
+
+ auto& from_regs = from_thread->regs();
+ auto& to_regs = to_thread->regs();
+
+ if (has_fxsr)
+ asm volatile("fxsave %0"
+ : "=m"(from_thread->fpu_state()));
+ else
+ asm volatile("fnsave %0"
+ : "=m"(from_thread->fpu_state()));
+
+#if ARCH(I386)
+ from_regs.fs = get_fs();
+ from_regs.gs = get_gs();
+ set_fs(to_regs.fs);
+ set_gs(to_regs.gs);
+#endif
+
+ if (from_thread->process().is_traced())
+ read_debug_registers_into(from_thread->debug_register_state());
+
+ if (to_thread->process().is_traced()) {
+ write_debug_registers_from(to_thread->debug_register_state());
+ } else {
+ clear_debug_registers();
+ }
+
+ auto& processor = Processor::current();
+#if ARCH(I386)
+ auto& tls_descriptor = processor.get_gdt_entry(GDT_SELECTOR_TLS);
+ tls_descriptor.set_base(to_thread->thread_specific_data());
+ tls_descriptor.set_limit(to_thread->thread_specific_region_size());
+#endif
+
+ if (from_regs.cr3 != to_regs.cr3)
+ write_cr3(to_regs.cr3);
+
+ to_thread->set_cpu(processor.get_id());
+ processor.restore_in_critical(to_thread->saved_critical());
+
+ if (has_fxsr)
+ asm volatile("fxrstor %0" ::"m"(to_thread->fpu_state()));
+ else
+ asm volatile("frstor %0" ::"m"(to_thread->fpu_state()));
+
+ // TODO: ioperm?
+}
+
+extern "C" u32 do_init_context(Thread* thread, u32 flags)
+{
+ VERIFY_INTERRUPTS_DISABLED();
+#if ARCH(I386)
+ thread->regs().eflags = flags;
+#else
+ thread->regs().rflags = flags;
+#endif
+ return Processor::current().init_context(*thread, true);
+}
}
diff --git a/Kernel/Arch/x86/i386/CPU.cpp b/Kernel/Arch/x86/i386/CPU.cpp
deleted file mode 100644
index 27636b9ad9..0000000000
--- a/Kernel/Arch/x86/i386/CPU.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#include <AK/Assertions.h>
-#include <AK/Types.h>
-
-#include <Kernel/Arch/x86/CPU.h>
-#include <Kernel/Arch/x86/Processor.h>
-#include <Kernel/Arch/x86/TrapFrame.h>
-#include <Kernel/KSyms.h>
-#include <Kernel/Process.h>
-#include <Kernel/Thread.h>
-
-namespace Kernel {
-
-// The compiler can't see the calls to these functions inside assembly.
-// Declare them, to avoid dead code warnings.
-extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread) __attribute__((used));
-extern "C" u32 do_init_context(Thread* thread, u32 flags) __attribute__((used));
-
-extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread)
-{
- VERIFY(from_thread == to_thread || from_thread->state() != Thread::Running);
- VERIFY(to_thread->state() == Thread::Running);
-
- bool has_fxsr = Processor::current().has_feature(CPUFeature::FXSR);
- Processor::set_current_thread(*to_thread);
-
- auto& from_regs = from_thread->regs();
- auto& to_regs = to_thread->regs();
-
- if (has_fxsr)
- asm volatile("fxsave %0"
- : "=m"(from_thread->fpu_state()));
- else
- asm volatile("fnsave %0"
- : "=m"(from_thread->fpu_state()));
-
- from_regs.fs = get_fs();
- from_regs.gs = get_gs();
- set_fs(to_regs.fs);
- set_gs(to_regs.gs);
-
- if (from_thread->process().is_traced())
- read_debug_registers_into(from_thread->debug_register_state());
-
- if (to_thread->process().is_traced()) {
- write_debug_registers_from(to_thread->debug_register_state());
- } else {
- clear_debug_registers();
- }
-
- auto& processor = Processor::current();
- auto& tls_descriptor = processor.get_gdt_entry(GDT_SELECTOR_TLS);
- tls_descriptor.set_base(to_thread->thread_specific_data());
- tls_descriptor.set_limit(to_thread->thread_specific_region_size());
-
- if (from_regs.cr3 != to_regs.cr3)
- write_cr3(to_regs.cr3);
-
- to_thread->set_cpu(processor.get_id());
- processor.restore_in_critical(to_thread->saved_critical());
-
- if (has_fxsr)
- asm volatile("fxrstor %0" ::"m"(to_thread->fpu_state()));
- else
- asm volatile("frstor %0" ::"m"(to_thread->fpu_state()));
-
- // TODO: ioperm?
-}
-
-extern "C" u32 do_init_context(Thread* thread, u32 flags)
-{
- VERIFY_INTERRUPTS_DISABLED();
- thread->regs().eflags = flags;
- return Processor::current().init_context(*thread, true);
-}
-
-}
diff --git a/Kernel/Arch/x86/i386/Processor.cpp b/Kernel/Arch/x86/i386/Processor.cpp
index 2719c52cb3..f2d46fa943 100644
--- a/Kernel/Arch/x86/i386/Processor.cpp
+++ b/Kernel/Arch/x86/i386/Processor.cpp
@@ -193,8 +193,7 @@ void Processor::switch_context(Thread*& from_thread, Thread*& to_thread)
// Switch to new thread context, passing from_thread and to_thread
// through to the new context using registers edx and eax
asm volatile(
- // NOTE: changing how much we push to the stack affects
- // SWITCH_CONTEXT_TO_STACK_SIZE and thread_context_first_enter()!
+ // NOTE: changing how much we push to the stack affects thread_context_first_enter()!
"pushfl \n"
"pushl %%ebx \n"
"pushl %%esi \n"
diff --git a/Kernel/Arch/x86/x86_64/CPU.cpp b/Kernel/Arch/x86/x86_64/CPU.cpp
deleted file mode 100644
index 4c02848a52..0000000000
--- a/Kernel/Arch/x86/x86_64/CPU.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#include <AK/Assertions.h>
-#include <AK/Types.h>
-
-#include <Kernel/Arch/x86/CPU.h>
-#include <Kernel/Arch/x86/Processor.h>
-#include <Kernel/Arch/x86/TrapFrame.h>
-#include <Kernel/KSyms.h>
-#include <Kernel/Process.h>
-#include <Kernel/Thread.h>
-
-namespace Kernel {
-
-// The compiler can't see the calls to these functions inside assembly.
-// Declare them, to avoid dead code warnings.
-extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread) __attribute__((used));
-extern "C" u32 do_init_context(Thread* thread, u32 flags) __attribute__((used));
-
-extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread)
-{
- (void)from_thread;
- (void)to_thread;
- TODO();
-}
-
-extern "C" u32 do_init_context(Thread* thread, u32 flags)
-{
- (void)thread;
- (void)flags;
- TODO();
-}
-}
diff --git a/Kernel/Arch/x86/x86_64/Processor.cpp b/Kernel/Arch/x86/x86_64/Processor.cpp
index 0828506d36..104202cb95 100644
--- a/Kernel/Arch/x86/x86_64/Processor.cpp
+++ b/Kernel/Arch/x86/x86_64/Processor.cpp
@@ -91,9 +91,8 @@ u32 Processor::init_context(Thread& thread, bool leave_crit)
auto& regs = thread.regs();
bool return_to_user = (regs.cs & 3) != 0;
- stack_top -= 2 * sizeof(u64);
- *reinterpret_cast<u64*>(kernel_stack_top - 2 * sizeof(u64)) = regs.rsp;
- *reinterpret_cast<u64*>(kernel_stack_top - 3 * sizeof(u64)) = FlatPtr(&exit_kernel_thread);
+ stack_top -= 1 * sizeof(u64);
+ *reinterpret_cast<u64*>(kernel_stack_top - 2 * sizeof(u64)) = FlatPtr(&exit_kernel_thread);
stack_top -= sizeof(RegisterState);
@@ -167,7 +166,71 @@ void Processor::switch_context(Thread*& from_thread, Thread*& to_thread)
dbgln_if(CONTEXT_SWITCH_DEBUG, "switch_context --> switching out of: {} {}", VirtualAddress(from_thread), *from_thread);
from_thread->save_critical(m_in_critical);
- PANIC("Context switching not implemented.");
+ // clang-format off
+ // Switch to new thread context, passing from_thread and to_thread
+ // through to the new context using registers rdx and rax
+ asm volatile(
+ // NOTE: changing how much we push to the stack affects thread_context_first_enter()!
+ "pushfq \n"
+ "pushq %%rbx \n"
+ "pushq %%rcx \n"
+ "pushq %%rbp \n"
+ "pushq %%rsi \n"
+ "pushq %%rdi \n"
+ "pushq %%r8 \n"
+ "pushq %%r9 \n"
+ "pushq %%r10 \n"
+ "pushq %%r11 \n"
+ "pushq %%r12 \n"
+ "pushq %%r13 \n"
+ "pushq %%r14 \n"
+ "pushq %%r15 \n"
+ "movq %%rsp, %[from_rsp] \n"
+ "movabs $1f, %%rbx \n"
+ "movq %%rbx, %[from_rip] \n"
+ "movq %[to_rsp0], %%rbx \n"
+ "movl %%ebx, %[tss_rsp0l] \n"
+ "shrq $32, %%rbx \n"
+ "movl %%ebx, %[tss_rsp0h] \n"
+ "movq %[to_rsp], %%rsp \n"
+ "pushq %[to_thread] \n"
+ "pushq %[from_thread] \n"
+ "pushq %[to_rip] \n"
+ "cld \n"
+ "movq 16(%%rsp), %%rsi \n"
+ "movq 8(%%rsp), %%rdi \n"
+ "jmp enter_thread_context \n"
+ "1: \n"
+ "popq %%rdx \n"
+ "popq %%rax \n"
+ "popq %%r15 \n"
+ "popq %%r14 \n"
+ "popq %%r13 \n"
+ "popq %%r12 \n"
+ "popq %%r11 \n"
+ "popq %%r10 \n"
+ "popq %%r9 \n"
+ "popq %%r8 \n"
+ "popq %%rdi \n"
+ "popq %%rsi \n"
+ "popq %%rbp \n"
+ "popq %%rcx \n"
+ "popq %%rbx \n"
+ "popfq \n"
+ : [from_rsp] "=m" (from_thread->regs().rsp),
+ [from_rip] "=m" (from_thread->regs().rip),
+ [tss_rsp0l] "=m" (m_tss.rsp0l),
+ [tss_rsp0h] "=m" (m_tss.rsp0h),
+ "=d" (from_thread), // needed so that from_thread retains the correct value
+ "=a" (to_thread) // needed so that to_thread retains the correct value
+ : [to_rsp] "g" (to_thread->regs().rsp),
+ [to_rsp0] "g" (to_thread->regs().rsp0),
+ [to_rip] "c" (to_thread->regs().rip),
+ [from_thread] "d" (from_thread),
+ [to_thread] "a" (to_thread)
+ : "memory", "rbx"
+ );
+ // clang-format on
dbgln_if(CONTEXT_SWITCH_DEBUG, "switch_context <-- from {} {} to {} {}", VirtualAddress(from_thread), *from_thread, VirtualAddress(to_thread), *to_thread);