Files
banan-os/kernel/kernel/Thread.cpp
Bananymous 6d1696b77e Kernel: Don't allocate userspace stacks in Thread
Thread only cares about stack's vaddr and size. Stacks are now allocated
before creating the threads.
2026-07-02 20:02:24 +03:00

819 lines
22 KiB
C++

#include <BAN/Errors.h>
#include <BAN/ScopeGuard.h>
#include <kernel/GDT.h>
#include <kernel/InterruptController.h>
#include <kernel/InterruptStack.h>
#include <kernel/Memory/kmalloc.h>
#include <kernel/Memory/MemoryBackedRegion.h>
#include <kernel/Process.h>
#include <kernel/Scheduler.h>
#include <kernel/Thread.h>
#include <kernel/Timer/Timer.h>
#include <kernel/UserCopy.h>
#include <sys/syscall.h>
namespace Kernel
{
static_assert(SYS_SIGPROCMASK == 78, "this is hard coded in arch/*/Signal.S");
static_assert(SIG_SETMASK == 3, "this is hard coded in arch/*/Signal.S");
extern "C" [[noreturn]] void start_kernel_thread();
extern "C" [[noreturn]] void start_userspace_thread();
extern "C" void signal_trampoline();
template<typename T>
static void write_to_stack(uintptr_t& rsp, const T& value) requires(sizeof(T) <= sizeof(uintptr_t))
{
rsp -= sizeof(uintptr_t);
*(uintptr_t*)rsp = (uintptr_t)value;
}
static pid_t s_next_tid = 1;
alignas(16) static uint8_t s_default_sse_storage[512];
static BAN::Atomic<bool> s_default_sse_storage_initialized = false;
static void initialize_default_sse_storage()
{
static BAN::Atomic<bool> is_initializing { false };
bool expected { false };
if (!is_initializing.compare_exchange(expected, true))
{
while (!s_default_sse_storage_initialized)
__builtin_ia32_pause();
asm volatile("" ::: "memory");
return;
}
const auto state = Processor::get_interrupt_state();
Processor::set_interrupt_state(InterruptState::Disabled);
Processor::enable_sse();
const uint32_t mxcsr = 0x1F80;
asm volatile(
"finit;"
"ldmxcsr %[mxcsr];"
#if ARCH(x86_64)
"fxsave64 %[storage];"
#elif ARCH(i686)
"fxsave %[storage];"
#else
#error
#endif
: [storage]"=m"(s_default_sse_storage)
: [mxcsr]"m"(mxcsr)
);
Processor::disable_sse();
Processor::set_interrupt_state(state);
s_default_sse_storage_initialized = true;
}
bool Thread::is_stopping_signal(int signal)
{
switch(signal)
{
case SIGSTOP:
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
return true;
default:
return false;
}
}
bool Thread::is_continuing_signal(int signal)
{
switch(signal)
{
case SIGCONT:
return true;
default:
return false;
}
}
bool Thread::is_terminating_signal(int signal)
{
switch (signal)
{
case SIGALRM:
case SIGHUP:
case SIGINT:
case SIGKILL:
case SIGPIPE:
case SIGTERM:
case SIGUSR1:
case SIGUSR2:
case SIGPOLL:
case SIGPROF:
case SIGVTALRM:
return true;
default:
return false;
}
}
bool Thread::is_abnormal_terminating_signal(int signal)
{
switch (signal)
{
case SIGABRT:
case SIGBUS:
case SIGFPE:
case SIGILL:
case SIGQUIT:
case SIGSEGV:
case SIGSYS:
case SIGTRAP:
case SIGXCPU:
case SIGXFSZ:
return true;
default:
return false;
}
}
bool Thread::is_realtime_signal(int signal)
{
return SIGRTMIN <= signal && signal <= SIGRTMAX;
}
static bool is_default_ignored_signal(int signal)
{
switch (signal)
{
case SIGCHLD:
case SIGURG:
case SIGWINCH:
case SIGCANCEL:
return true;
default:
return Thread::is_realtime_signal(signal);
}
}
BAN::ErrorOr<Thread*> Thread::create_kernel(entry_t entry, void* data)
{
// Create the thread object
Thread* thread = new Thread(s_next_tid++, nullptr);
if (thread == nullptr)
return BAN::Error::from_errno(ENOMEM);
BAN::ScopeGuard thread_deleter([thread] { delete thread; });
// Initialize stack and registers
thread->m_kernel_stack = TRY(VirtualRange::create_to_vaddr_range(
PageTable::kernel(),
{ KERNEL_OFFSET, UINTPTR_MAX },
kernel_stack_size,
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
true
));
// Initialize stack for returning
uintptr_t sp = thread->kernel_stack_top();
write_to_stack(sp, thread);
write_to_stack(sp, &Thread::on_exit_trampoline);
write_to_stack(sp, data);
write_to_stack(sp, entry);
thread->m_yield_registers = {};
thread->m_yield_registers.ip = reinterpret_cast<vaddr_t>(start_kernel_thread);
thread->m_yield_registers.sp = sp;
thread_deleter.disable();
return thread;
}
BAN::ErrorOr<Thread*> Thread::create_userspace(Process* process, PageTable& page_table, vaddr_t userspace_stack_vaddr, size_t userspace_stack_size, vaddr_t entry_point, vaddr_t stack_pointer)
{
ASSERT(process);
// Create the thread object
Thread* thread = new Thread(s_next_tid++, process);
if (thread == nullptr)
return BAN::Error::from_errno(ENOMEM);
BAN::ScopeGuard thread_deleter([thread] { delete thread; });
thread->m_is_userspace = true;
thread->m_kernel_stack = TRY(VirtualRange::create_to_vaddr_range(
page_table,
{ userspace_stack_base, USERSPACE_END },
kernel_stack_size,
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
true
));
thread->m_userspace_stack_vaddr = userspace_stack_vaddr;
thread->m_userspace_stack_size = userspace_stack_size;
// Initialize stack for returning
PageTable::with_fast_page(thread->kernel_stack().paddr_of(thread->kernel_stack_top() - PAGE_SIZE), [=] {
uintptr_t cur_sp = PageTable::fast_page() + PAGE_SIZE;
write_to_stack(cur_sp, 0x20 | 3);
write_to_stack(cur_sp, stack_pointer);
write_to_stack(cur_sp, 0x202);
#if ARCH(x86_64)
write_to_stack(cur_sp, 0x28 | 3);
#elif ARCH(i686)
write_to_stack(cur_sp, 0x18 | 3);
#endif
write_to_stack(cur_sp, entry_point);
});
thread->m_yield_registers = {};
thread->m_yield_registers.ip = reinterpret_cast<vaddr_t>(start_userspace_thread);
thread->m_yield_registers.sp = thread->kernel_stack_top() - 5 * sizeof(uintptr_t);
thread_deleter.disable();
return thread;
}
Thread::Thread(pid_t tid, Process* process)
: m_tid(tid), m_process(process)
{
if (!s_default_sse_storage_initialized)
initialize_default_sse_storage();
memcpy(m_sse_storage, s_default_sse_storage, sizeof(m_sse_storage));
}
Thread& Thread::current()
{
return Processor::scheduler().current_thread();
}
pid_t Thread::current_tid()
{
if (Processor::count() == 0)
return 0;
return Processor::scheduler().current_tid();
}
Process& Thread::process()
{
ASSERT(m_process);
return *m_process;
}
const Process& Thread::process() const
{
ASSERT(m_process);
return *m_process;
}
Thread::~Thread()
{
if (Processor::get_current_sse_thread() == this)
{
Processor::set_current_sse_thread(nullptr);
Processor::disable_sse();
}
if (m_delete_process)
{
ASSERT(m_process);
delete m_process;
}
}
uint64_t Thread::cpu_time_ns() const
{
SpinLockGuard _(m_cpu_time_lock);
if (m_cpu_time_start_ns == UINT64_MAX)
return m_cpu_time_ns;
return m_cpu_time_ns + (SystemTimer::get().ns_since_boot() - m_cpu_time_start_ns);
}
void Thread::set_cpu_time_start()
{
SpinLockGuard _(m_cpu_time_lock);
ASSERT(m_cpu_time_start_ns == UINT64_MAX);
m_cpu_time_start_ns = SystemTimer::get().ns_since_boot();
}
void Thread::set_cpu_time_stop()
{
SpinLockGuard _(m_cpu_time_lock);
ASSERT(m_cpu_time_start_ns != UINT64_MAX);
m_cpu_time_ns += SystemTimer::get().ns_since_boot() - m_cpu_time_start_ns;
m_cpu_time_start_ns = UINT64_MAX;
}
void Thread::update_processor_index_address()
{
if (!is_userspace() || !has_process())
return;
Processor::gdt().set_cpu_index(Processor::current_index());
}
BAN::ErrorOr<Thread*> Thread::clone(Process* new_process, uintptr_t sp, uintptr_t ip)
{
ASSERT(m_is_userspace);
ASSERT(m_state == State::Executing);
Thread* thread = new Thread(s_next_tid++, new_process);
if (thread == nullptr)
return BAN::Error::from_errno(ENOMEM);
BAN::ScopeGuard thread_deleter([thread] { delete thread; });
thread->m_is_userspace = true;
thread->m_kernel_stack = TRY(VirtualRange::create_to_vaddr_range(
new_process->page_table(),
{ userspace_stack_base, USERSPACE_END },
kernel_stack_size,
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
true
));
// NOTE: copy [sp, stack_end] so fork return works
PageTable::with_fast_page(thread->m_kernel_stack->paddr_of(thread->kernel_stack_top() - PAGE_SIZE), [&] {
const size_t ncopy = kernel_stack_top() - sp;
ASSERT(ncopy <= PAGE_SIZE);
memcpy(
PageTable::fast_page_as_ptr(PAGE_SIZE - ncopy),
reinterpret_cast<void*>(sp),
ncopy
);
});
thread->m_userspace_stack_vaddr = m_userspace_stack_vaddr;
thread->m_userspace_stack_size = m_userspace_stack_size;
thread->m_fsbase = m_fsbase;
thread->m_gsbase = m_gsbase;
thread->m_state = State::NotStarted;
if (Processor::get_current_sse_thread() == this)
save_sse();
memcpy(thread->m_sse_storage, m_sse_storage, sizeof(m_sse_storage));
thread->m_yield_registers = {};
thread->m_yield_registers.ip = ip;
thread->m_yield_registers.sp = sp;
thread->m_yield_registers.ret = 0;
thread_deleter.disable();
return thread;
}
void Thread::setup_process_cleanup()
{
ASSERT(Processor::get_interrupt_state() == InterruptState::Disabled);
static entry_t entry = [](void* process_ptr) {
auto* thread = &Thread::current();
auto* process = static_cast<Process*>(process_ptr);
ASSERT(thread->m_process == process);
process->cleanup_function(thread);
thread->m_delete_process = true;
// will call on thread exit after return
};
m_state = State::NotStarted;
m_signal_pending_mask = 0;
m_signal_block_mask = ~0ull;
PageTable::with_fast_page(kernel_stack().paddr_of(kernel_stack_top() - PAGE_SIZE), [&] {
uintptr_t sp = PageTable::fast_page() + PAGE_SIZE;
write_to_stack(sp, this);
write_to_stack(sp, &Thread::on_exit_trampoline);
write_to_stack(sp, m_process);
write_to_stack(sp, entry);
});
m_yield_registers = {};
m_yield_registers.ip = reinterpret_cast<vaddr_t>(start_kernel_thread);
m_yield_registers.sp = kernel_stack_top() - 4 * sizeof(uintptr_t);
}
bool Thread::is_interrupted_by_signal(bool skip_stop_and_cont) const
{
if (!is_userspace() || m_state != State::Executing)
return false;
auto& interrupt_stack = *reinterpret_cast<InterruptStack*>(kernel_stack_top() - sizeof(InterruptStack));
if (!GDT::is_user_segment(interrupt_stack.cs))
return false;
uint64_t full_pending_mask = m_signal_pending_mask | process().signal_pending_mask();
uint64_t signals = full_pending_mask & ~m_signal_block_mask;
for (uint8_t i = 0; i < _SIGMAX; i++)
{
if (!(signals & ((uint64_t)1 << i)))
continue;
if (skip_stop_and_cont && (is_stopping_signal(i) || is_continuing_signal(i)))
continue;
vaddr_t signal_handler;
{
SpinLockGuard _(m_process->m_signal_lock);
const auto& handler = m_process->m_signal_handlers[i];
signal_handler = (handler.sa_flags & SA_SIGINFO)
? reinterpret_cast<vaddr_t>(handler.sa_sigaction)
: reinterpret_cast<vaddr_t>(handler.sa_handler);
}
if (signal_handler == reinterpret_cast<vaddr_t>(SIG_IGN))
continue;
if (signal_handler == reinterpret_cast<vaddr_t>(SIG_DFL) && is_default_ignored_signal(i))
continue;
return true;
}
return false;
}
bool Thread::will_exit_because_of_signal() const
{
const uint64_t full_pending_mask = m_signal_pending_mask | process().signal_pending_mask();
const uint64_t signals = full_pending_mask & ~m_signal_block_mask;
for (size_t sig = _SIGMIN; sig <= _SIGMAX; sig++)
{
if (!(signals & (static_cast<uint64_t>(1) << sig)))
continue;
if (!is_terminating_signal(sig) && !is_abnormal_terminating_signal(sig))
continue;
SpinLockGuard _(m_process->m_signal_lock);
if (m_process->m_signal_handlers[sig].sa_handler == SIG_DFL)
return true;
}
return false;
}
bool Thread::handle_signal_if_interrupted()
{
int signal;
siginfo_t signal_info;
signal_handle_info_t handle_info;
{
SpinLockGuard _1(m_signal_lock);
SpinLockGuard _2(m_process->m_signal_lock);
const uint64_t process_signal_pending_mask = m_process->m_signal_pending_mask;
const uint64_t full_pending_mask = (m_signal_pending_mask | process_signal_pending_mask) & ~m_signal_block_mask;
if (full_pending_mask == 0) [[likely]]
return false;
for (signal = _SIGMIN; signal <= _SIGMAX; signal++)
if (full_pending_mask & (1ull << signal))
break;
ASSERT(signal <= _SIGMAX);
if (process_signal_pending_mask & (1ull << signal))
signal_info = m_process->m_signal_infos[signal];
else
signal_info = m_signal_infos[signal];
signal_info.si_signo = signal;
handle_info = remove_signal_and_get_info(signal);
}
handle_signal_impl(signal, signal_info, handle_info);
return handle_info.has_sa_restart;
}
bool Thread::handle_signal(int signal, const siginfo_t& signal_info)
{
ASSERT(&Thread::current() == this);
ASSERT(is_userspace());
ASSERT(signal >= _SIGMIN);
ASSERT(signal <= _SIGMAX);
signal_handle_info_t handle_info;
// If this signal is blocked or ignored, terminate the process
bool terminate_process = false;
{
SpinLockGuard _1(m_signal_lock);
SpinLockGuard _2(m_process->m_signal_lock);
if (m_signal_block_mask & (1ull << signal))
terminate_process = true;
handle_info = remove_signal_and_get_info(signal);
if (handle_info.handler == reinterpret_cast<vaddr_t>(SIG_IGN))
terminate_process = true;
}
if (terminate_process)
{
process().exit(128 + signal, signal | 0x80);
ASSERT_NOT_REACHED();
}
handle_signal_impl(signal, signal_info, handle_info);
return handle_info.has_sa_restart;
}
Thread::signal_handle_info_t Thread::remove_signal_and_get_info(int signal)
{
ASSERT(m_signal_lock.current_processor_has_lock());
ASSERT(m_process->m_signal_lock.current_processor_has_lock());
ASSERT(signal >= _SIGMIN);
ASSERT(signal <= _SIGMAX);
const uint64_t restore_sigmask = m_signal_block_mask;
auto& handler = m_process->m_signal_handlers[signal];
const vaddr_t signal_handler = (handler.sa_flags & SA_SIGINFO)
? reinterpret_cast<vaddr_t>(handler.sa_sigaction)
: reinterpret_cast<vaddr_t>(handler.sa_handler);
const bool has_sa_restart = !!(handler.sa_flags & SA_RESTART);
vaddr_t signal_stack_top = 0;
if (m_signal_alt_stack.ss_flags != SS_DISABLE && (handler.sa_flags & SA_ONSTACK) && !currently_on_alternate_stack())
signal_stack_top = reinterpret_cast<vaddr_t>(m_signal_alt_stack.ss_sp) + m_signal_alt_stack.ss_size;
m_signal_block_mask |= handler.sa_mask;
if (!(handler.sa_flags & SA_NODEFER))
m_signal_block_mask |= 1ull << signal;
if (handler.sa_flags & SA_RESETHAND)
handler = { .sa_handler = SIG_DFL, .sa_mask = 0, .sa_flags = 0 };
m_signal_pending_mask &= ~(1ull << signal);
m_process->m_signal_pending_mask &= ~(1ull << signal);
if (m_signal_suspend_mask.has_value())
{
m_signal_block_mask = m_signal_suspend_mask.value();
m_signal_suspend_mask.clear();
}
return {
.handler = signal_handler,
.stack_top = signal_stack_top,
.restore_sigmask = restore_sigmask,
.has_sa_restart = has_sa_restart,
};
}
void Thread::handle_signal_impl(int signal, const siginfo_t& signal_info, const signal_handle_info_t& handle_info)
{
ASSERT(this == &Thread::current());
ASSERT(is_userspace());
ASSERT(Processor::get_interrupt_state() == InterruptState::Enabled);
auto& interrupt_stack = *reinterpret_cast<InterruptStack*>(kernel_stack_top() - sizeof(InterruptStack));
ASSERT(GDT::is_user_segment(interrupt_stack.cs));
if (handle_info.handler == reinterpret_cast<vaddr_t>(SIG_IGN))
;
else if (handle_info.handler != reinterpret_cast<vaddr_t>(SIG_DFL))
{
// call userspace signal handlers
#if ARCH(x86_64)
interrupt_stack.sp -= 128; // skip possible red-zone
#endif
const auto write_to_stack =
[&]<typename T>(uintptr_t& sp, const T& value)
{
static_assert(sizeof(T) >= sizeof(uintptr_t));
sp -= sizeof(T);
if (write_to_user(reinterpret_cast<void*>(sp), &value, sizeof(T)).is_error())
m_process->exit(128 + SIGSEGV, SIGSEGV | 0x80);
};
write_to_stack(interrupt_stack.sp, interrupt_stack.ip);
const vaddr_t old_stack = interrupt_stack.sp;
if (handle_info.stack_top)
interrupt_stack.sp = handle_info.stack_top;
write_to_stack(interrupt_stack.sp, old_stack);
write_to_stack(interrupt_stack.sp, interrupt_stack.flags);
write_to_stack(interrupt_stack.sp, handle_info.restore_sigmask);
ASSERT(signal_info.si_signo == signal);
write_to_stack(interrupt_stack.sp, signal_info);
write_to_stack(interrupt_stack.sp, static_cast<uintptr_t>(signal));
write_to_stack(interrupt_stack.sp, handle_info.handler);
interrupt_stack.ip = (uintptr_t)signal_trampoline;
}
else
{
if (is_abnormal_terminating_signal(signal))
{
process().exit(128 + signal, signal | 0x80);
ASSERT_NOT_REACHED();
}
else if (is_terminating_signal(signal))
{
process().exit(128 + signal, signal);
ASSERT_NOT_REACHED();
}
else if (is_stopping_signal(signal))
{
process().set_stopped(true, signal);
}
else if (is_continuing_signal(signal))
{
process().set_stopped(false, signal);
}
else if (is_default_ignored_signal(signal))
{
}
else
{
panic("Executing unhandled signal {}", signal);
}
}
}
void Thread::add_signal(int signal, const siginfo_t& info)
{
SpinLockGuard _(m_signal_lock);
if (m_process)
{
vaddr_t signal_handler;
{
SpinLockGuard _(m_process->m_signal_lock);
const auto& handler = m_process->m_signal_handlers[signal];
signal_handler = (handler.sa_flags & SA_SIGINFO)
? reinterpret_cast<vaddr_t>(handler.sa_sigaction)
: reinterpret_cast<vaddr_t>(handler.sa_handler);
}
if (signal_handler == reinterpret_cast<vaddr_t>(SIG_IGN))
return;
if (signal_handler == reinterpret_cast<vaddr_t>(SIG_DFL) && is_default_ignored_signal(signal))
return;
}
const uint64_t mask = 1ull << signal;
m_signal_pending_mask |= mask;
m_signal_infos[signal] = info;
if (this != &Thread::current())
Processor::scheduler().unblock_thread(this);
}
void Thread::set_suspend_signal_mask(uint64_t sigmask)
{
SpinLockGuard _(m_signal_lock);
ASSERT(!m_signal_suspend_mask.has_value());
m_signal_suspend_mask = m_signal_block_mask;
m_signal_block_mask = sigmask;
}
bool Thread::currently_on_alternate_stack() const
{
ASSERT(m_signal_lock.current_processor_has_lock());
if (m_signal_alt_stack.ss_flags == SS_ONSTACK)
return false;
const vaddr_t stack_bottom = reinterpret_cast<vaddr_t>(m_signal_alt_stack.ss_sp);
const vaddr_t stack_top = stack_bottom + m_signal_alt_stack.ss_size;
const vaddr_t sp = m_yield_registers.sp;
return stack_bottom <= sp && sp <= stack_top;
}
BAN::ErrorOr<void> Thread::sigaltstack(const stack_t* ss, stack_t* oss)
{
SpinLockGuard _(m_signal_lock);
const bool on_alt_stack = currently_on_alternate_stack();
if (oss)
{
*oss = m_signal_alt_stack;
if (on_alt_stack)
oss->ss_flags = SS_ONSTACK;
}
if (ss)
{
if (on_alt_stack)
return BAN::Error::from_errno(EPERM);
if (ss->ss_flags && ss->ss_flags != SS_DISABLE)
return BAN::Error::from_errno(EINVAL);
if (ss->ss_size < MINSIGSTKSZ)
return BAN::Error::from_errno(ENOMEM);
m_signal_alt_stack = *ss;
}
return {};
}
BAN::ErrorOr<void> Thread::sleep_or_eintr_for_ns(uint64_t timeout_ns)
{
if (is_interrupted_by_signal(true))
return BAN::Error::from_errno(EINTR);
SystemTimer::get().sleep_for_ns(timeout_ns);
if (is_interrupted_by_signal(true))
return BAN::Error::from_errno(EINTR);
return {};
}
BAN::ErrorOr<void> Thread::sleep_or_eintr_until_ns(uint64_t waketime_ns)
{
if (is_interrupted_by_signal(true))
return BAN::Error::from_errno(EINTR);
SystemTimer::get().sleep_until_ns(waketime_ns);
if (is_interrupted_by_signal(true))
return BAN::Error::from_errno(EINTR);
return {};
}
BAN::ErrorOr<void> Thread::block_or_eintr_indefinite(ThreadBlocker& thread_blocker, BaseMutex* mutex)
{
if (is_interrupted_by_signal(true))
return BAN::Error::from_errno(EINTR);
thread_blocker.block_indefinite(mutex);
if (is_interrupted_by_signal(true))
return BAN::Error::from_errno(EINTR);
return {};
}
BAN::ErrorOr<void> Thread::block_or_eintr_or_timeout_ns(ThreadBlocker& thread_blocker, uint64_t timeout_ns, bool etimedout, BaseMutex* mutex)
{
const uint64_t wake_time_ns = SystemTimer::get().ns_since_boot() + timeout_ns;
return block_or_eintr_or_waketime_ns(thread_blocker, wake_time_ns, etimedout, mutex);
}
BAN::ErrorOr<void> Thread::block_or_eintr_or_waketime_ns(ThreadBlocker& thread_blocker, uint64_t wake_time_ns, bool etimedout, BaseMutex* mutex)
{
if (is_interrupted_by_signal(true))
return BAN::Error::from_errno(EINTR);
thread_blocker.block_with_wake_time_ns(wake_time_ns, mutex);
if (is_interrupted_by_signal(true))
return BAN::Error::from_errno(EINTR);
if (etimedout && SystemTimer::get().ms_since_boot() >= wake_time_ns)
return BAN::Error::from_errno(ETIMEDOUT);
return {};
}
void Thread::on_exit_trampoline(Thread* thread)
{
thread->on_exit();
}
void Thread::on_exit()
{
ASSERT(this == &Thread::current());
if (!m_delete_process && has_process())
{
if (process().on_thread_exit(*this))
{
Processor::set_interrupt_state(InterruptState::Disabled);
setup_process_cleanup();
Processor::yield();
ASSERT_NOT_REACHED();
}
}
Processor::set_interrupt_state(InterruptState::Disabled);
m_state = State::Terminated;
Processor::yield();
ASSERT_NOT_REACHED();
}
void Thread::save_sse()
{
#if ARCH(x86_64)
__builtin_ia32_fxsave64(m_sse_storage);
#elif ARCH(i686)
// no idea why the builtin don't work
asm volatile("fxsave %0" :: "m"(m_sse_storage));
#else
#error
#endif
}
void Thread::load_sse()
{
#if ARCH(x86_64)
__builtin_ia32_fxrstor64(m_sse_storage);
#elif ARCH(i686)
// no idea why the builtin don't work
asm volatile("fxrstor %0" :: "m"(m_sse_storage));
#else
#error
#endif
}
}