/******************************************************************************* Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. This file is part of Sortix. Sortix is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Sortix is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Sortix. If not, see . thread.cpp Describes a thread belonging to a process. *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Sortix { Thread::Thread() { id = 0; // TODO: Make a thread id. process = NULL; prevsibling = NULL; nextsibling = NULL; scheduler_list_prev = NULL; scheduler_list_next = NULL; state = NONE; memset(®isters, 0, sizeof(registers)); fsbase = 0; gsbase = 0; kernelstackpos = 0; kernelstacksize = 0; kernelstackmalloced = false; pledged_destruction = false; fpuinitialized = false; // If malloc isn't 16-byte aligned, then we can't rely on offsets in // our own class, so we'll just fix ourselves nicely up. unsigned long fpuaddr = ((unsigned long) fpuenv+16UL) & ~(16UL-1UL); fpuenvaligned = (uint8_t*) fpuaddr; sigemptyset(&signal_pending); sigemptyset(&signal_mask); memset(&signal_stack, 0, sizeof(signal_stack)); signal_stack.ss_flags = SS_DISABLE; } Thread::~Thread() { if ( process ) process->OnThreadDestruction(this); assert(CurrentThread() != this); if ( kernelstackmalloced ) delete[] (uint8_t*) kernelstackpos; } void Thread::SaveRegisters(const CPU::InterruptRegisters* src) { #if defined(__i386__) registers.eip = src->eip; registers.esp = src->esp; registers.eax = src->eax; registers.ebx = src->ebx; registers.ecx = src->ecx; registers.edx = src->edx; registers.edi = src->edi; registers.esi = src->esi; registers.ebp = src->ebp; registers.cs = src->cs; registers.ds = src->ds; registers.ss = src->ss; registers.eflags = src->eflags; registers.kerrno = src->kerrno; registers.signal_pending = src->signal_pending; #elif defined(__x86_64__) registers.rip = src->rip; registers.rsp = src->rsp; registers.rax = src->rax; registers.rbx = src->rbx; registers.rcx = src->rcx; registers.rdx = src->rdx; registers.rdi = src->rdi; registers.rsi = src->rsi; registers.rbp = src->rbp; registers.r8 = src->r8; registers.r9 = src->r9; registers.r10 = src->r10; registers.r11 = src->r11; registers.r12 = src->r12; registers.r13 = src->r13; registers.r14 = src->r14; registers.r15 = src->r15; registers.cs = src->cs; registers.ds = src->ds; registers.ss = src->ss; registers.rflags = src->rflags; registers.kerrno = src->kerrno; registers.signal_pending = src->signal_pending; #else #warning "You need to add register saving support" #endif } void Thread::LoadRegisters(CPU::InterruptRegisters* dest) { #if defined(__i386__) dest->eip = registers.eip; dest->esp = registers.esp; dest->eax = registers.eax; dest->ebx = registers.ebx; dest->ecx = registers.ecx; dest->edx = registers.edx; dest->edi = registers.edi; dest->esi = registers.esi; dest->ebp = registers.ebp; dest->cs = registers.cs; dest->ds = registers.ds; dest->ss = registers.ss; dest->eflags = registers.eflags; dest->kerrno = registers.kerrno; dest->signal_pending = registers.signal_pending; #elif defined(__x86_64__) dest->rip = registers.rip; dest->rsp = registers.rsp; dest->rax = registers.rax; dest->rbx = registers.rbx; dest->rcx = registers.rcx; dest->rdx = registers.rdx; dest->rdi = registers.rdi; dest->rsi = registers.rsi; dest->rbp = registers.rbp; dest->r8 = registers.r8; dest->r9 = registers.r9; dest->r10 = registers.r10; dest->r11 = registers.r11; dest->r12 = registers.r12; dest->r13 = registers.r13; dest->r14 = registers.r14; dest->r15 = registers.r15; dest->cs = registers.cs; dest->ds = registers.ds; dest->ss = registers.ss; dest->rflags = registers.rflags; dest->kerrno = registers.kerrno; dest->signal_pending = registers.signal_pending; #else #warning "You need to add register loading support" #endif } addr_t Thread::SwitchAddressSpace(addr_t newaddrspace) { bool wasenabled = Interrupt::SetEnabled(false); addr_t result = addrspace; addrspace = newaddrspace; Memory::SwitchAddressSpace(newaddrspace); Interrupt::SetEnabled(wasenabled); return result; } Thread* CreateKernelThread(Process* process, CPU::InterruptRegisters* regs, unsigned long fsbase, unsigned long gsbase) { #if defined(__x86_64__) if ( fsbase >> 48 != 0x0000 && fsbase >> 48 != 0xFFFF ) return errno = EINVAL, (Thread*) NULL; if ( gsbase >> 48 != 0x0000 && gsbase >> 48 != 0xFFFF ) return errno = EINVAL, (Thread*) NULL; #endif assert(process && regs && process->addrspace); Thread* thread = new Thread; if ( !thread ) return NULL; thread->addrspace = process->addrspace; thread->SaveRegisters(regs); thread->fsbase = fsbase; thread->gsbase = gsbase; kthread_mutex_lock(&process->threadlock); // Create the family tree. thread->process = process; Thread* firsty = process->firstthread; if ( firsty ) firsty->prevsibling = thread; thread->nextsibling = firsty; process->firstthread = thread; kthread_mutex_unlock(&process->threadlock); return thread; } static void SetupKernelThreadRegs(CPU::InterruptRegisters* regs, void (*entry)(void*), void* user, uintptr_t stack, size_t stack_size) { #if defined(__i386__) uintptr_t* stack_values = (uintptr_t*) (stack + stack_size); assert(!((uintptr_t) stack_values & 3UL)); assert(4 * sizeof(uintptr_t) <= stack_size); stack_values[-1] = (uintptr_t) 0; /* null eip */ stack_values[-2] = (uintptr_t) 0; /* null ebp */ stack_values[-3] = (uintptr_t) user; /* thread parameter */ stack_values[-4] = (uintptr_t) kthread_exit; /* return to kthread_exit */ regs->eip = (uintptr_t) entry; regs->esp = (uintptr_t) (stack_values - 4); regs->eax = 0; regs->ebx = 0; regs->ecx = 0; regs->edx = 0; regs->edi = 0; regs->esi = 0; regs->ebp = (uintptr_t) (stack_values - 2); regs->cs = KCS | KRPL; regs->ds = KDS | KRPL; regs->ss = KDS | KRPL; regs->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; regs->kerrno = 0; regs->signal_pending = 0; #elif defined(__x86_64__) if ( (stack & 15UL) == 8 && 8 <= stack_size ) { stack += 8; stack_size -= 8; } uintptr_t* stack_values = (uintptr_t*) (stack + stack_size); assert(!((uintptr_t) stack_values & 15UL)); assert(3 * sizeof(uintptr_t) <= stack_size); stack_values[-1] = (uintptr_t) 0; /* null rip */ stack_values[-2] = (uintptr_t) 0; /* null rbp */ stack_values[-3] = (uintptr_t) kthread_exit; /* return to kthread_exit */ regs->rip = (uintptr_t) entry; regs->rsp = (uintptr_t) (stack_values - 3); regs->rax = 0; regs->rbx = 0; regs->rcx = 0; regs->rdx = 0; regs->rdi = (uintptr_t) user; regs->rsi = 0; regs->rbp = 0; regs->r8 = 0; regs->r9 = 0; regs->r10 = 0; regs->r11 = 0; regs->r12 = 0; regs->r13 = 0; regs->r14 = 0; regs->r15 = 0; regs->cs = KCS | KRPL; regs->ds = KDS | KRPL; regs->ss = KDS | KRPL; regs->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; regs->kerrno = 0; regs->signal_pending = 0; #else #warning "You need to add thread register initialization support" #endif } Thread* CreateKernelThread(Process* process, void (*entry)(void*), void* user, size_t stacksize) { const size_t DEFAULT_KERNEL_STACK_SIZE = 8 * 1024UL; if ( !stacksize ) stacksize = DEFAULT_KERNEL_STACK_SIZE; uint8_t* stack = new uint8_t[stacksize]; if ( !stack ) return NULL; CPU::InterruptRegisters regs; SetupKernelThreadRegs(®s, entry, user, (uintptr_t) stack, stacksize); Thread* thread = CreateKernelThread(process, ®s, 0, 0); if ( !thread ) { delete[] stack; return NULL; } thread->kernelstackpos = (uintptr_t) stack; thread->kernelstacksize = stacksize; thread->kernelstackmalloced = true; return thread; } Thread* CreateKernelThread(void (*entry)(void*), void* user, size_t stacksize) { return CreateKernelThread(CurrentProcess(), entry, user, stacksize); } void StartKernelThread(Thread* thread) { Scheduler::SetThreadState(thread, ThreadState::RUNNABLE); } Thread* RunKernelThread(Process* process, CPU::InterruptRegisters* regs) { Thread* thread = CreateKernelThread(process, regs, 0, 0); if ( !thread ) return NULL; StartKernelThread(thread); return thread; } Thread* RunKernelThread(Process* process, void (*entry)(void*), void* user, size_t stacksize) { Thread* thread = CreateKernelThread(process, entry, user, stacksize); if ( !thread ) return NULL; StartKernelThread(thread); return thread; } Thread* RunKernelThread(void (*entry)(void*), void* user, size_t stacksize) { Thread* thread = CreateKernelThread(entry, user, stacksize); if ( !thread ) return NULL; StartKernelThread(thread); return thread; } static int sys_exit_thread(int requested_exit_code, int flags, const struct exit_thread* user_extended) { if ( flags & ~(EXIT_THREAD_ONLY_IF_OTHERS | EXIT_THREAD_UNMAP | EXIT_THREAD_ZERO | EXIT_THREAD_TLS_UNMAP | EXIT_THREAD_PROCESS | EXIT_THREAD_DUMP_CORE) ) return errno = EINVAL, -1; if ( (flags & EXIT_THREAD_ONLY_IF_OTHERS) && (flags & EXIT_THREAD_PROCESS) ) return errno = EINVAL, -1; Thread* thread = CurrentThread(); Process* process = CurrentProcess(); struct exit_thread extended; if ( !user_extended ) memset(&extended, 0, sizeof(extended)); else if ( !CopyFromUser(&extended, user_extended, sizeof(extended)) ) return -1; extended.unmap_size = Page::AlignUp(extended.unmap_size); kthread_mutex_lock(&thread->process->threadlock); bool is_others = false; for ( Thread* iter = thread->process->firstthread; !is_others && iter; iter = iter->nextsibling ) { if ( iter == thread ) continue; if ( iter->pledged_destruction ) continue; is_others = true; } if ( !(flags & EXIT_THREAD_ONLY_IF_OTHERS) || is_others ) thread->pledged_destruction = true; bool are_threads_exiting = false; if ( (flags & EXIT_THREAD_PROCESS) || !is_others ) process->threads_exiting = true; else if ( process->threads_exiting ) are_threads_exiting = true; kthread_mutex_unlock(&thread->process->threadlock); // Self-destruct if another thread began exiting the process. if ( are_threads_exiting ) kthread_exit(); if ( (flags & EXIT_THREAD_ONLY_IF_OTHERS) && !is_others ) return errno = ESRCH, -1; if ( flags & EXIT_THREAD_UNMAP && Page::IsAligned((uintptr_t) extended.unmap_from) && extended.unmap_size ) { ScopedLock lock(&process->segment_lock); Memory::UnmapMemory(process, (uintptr_t) extended.unmap_from, extended.unmap_size); Memory::Flush(); // TODO: The segment is not actually removed! } if ( flags & EXIT_THREAD_TLS_UNMAP && Page::IsAligned((uintptr_t) extended.tls_unmap_from) && extended.tls_unmap_size ) { ScopedLock lock(&process->segment_lock); Memory::UnmapMemory(process, (uintptr_t) extended.tls_unmap_from, extended.tls_unmap_size); Memory::Flush(); } if ( flags & EXIT_THREAD_ZERO ) ZeroUser(extended.zero_from, extended.zero_size); if ( !is_others ) { // Validate the requested exit code such that the process can't exit // with an impossible exit status or that it wasn't actually terminated. int the_nature = WNATURE(requested_exit_code); int the_status = WEXITSTATUS(requested_exit_code); int the_signal = WTERMSIG(requested_exit_code); if ( the_nature == WNATURE_EXITED ) the_signal = 0; else if ( the_nature == WNATURE_SIGNALED ) { if ( the_signal == 0 /* null signal */ || the_signal == SIGSTOP || the_signal == SIGTSTP || the_signal == SIGTTIN || the_signal == SIGTTOU || the_signal == SIGCONT ) the_signal = SIGKILL; the_status = 128 + the_signal; } else { the_nature = WNATURE_SIGNALED; the_signal = SIGKILL; } requested_exit_code = WCONSTRUCT(the_nature, the_status, the_signal); thread->process->ExitWithCode(requested_exit_code); } kthread_exit(); } void Thread::Init() { Syscall::Register(SYSCALL_EXIT_THREAD, (void*) sys_exit_thread); } } // namespace Sortix