diff --git a/repos/base/include/base/thread.h b/repos/base/include/base/thread.h index 4fe1609527..13c843593f 100644 --- a/repos/base/include/base/thread.h +++ b/repos/base/include/base/thread.h @@ -53,6 +53,8 @@ class Genode::Thread struct Stack_info { addr_t base; addr_t top; }; + struct Tls { struct Base; Base *ptr; }; + private: /** @@ -145,6 +147,17 @@ class Genode::Thread */ static Trace::Logger *_logger(); + /** + * Base pointer to thread-local storage + * + * The opaque pointer allows higher-level thread libraries (i.e., + * pthread) to implement TLS. It should never be used outside such + * libraries. + */ + Tls _tls { }; + + friend class Tls::Base; + /** * Hook for platform-specific constructor supplements * diff --git a/repos/libports/src/lib/pthread/thread.cc b/repos/libports/src/lib/pthread/thread.cc index c2de95200a..e26f325bc6 100644 --- a/repos/libports/src/lib/pthread/thread.cc +++ b/repos/libports/src/lib/pthread/thread.cc @@ -179,37 +179,35 @@ extern "C" { pthread_t pthread_self(void) { - Thread *myself = Thread::myself(); + try { + pthread_t pthread_myself = + static_cast(&Thread::Tls::Base::tls()); - pthread_t pthread_myself = static_cast(myself); - - if (pthread_registry().contains(pthread_myself)) - return pthread_myself; + if (pthread_registry().contains(pthread_myself)) + return pthread_myself; + } + catch (Thread::Tls::Base::Undefined) { } /* * We pass here if the main thread or an alien thread calls * pthread_self(). So check for aliens (or other bugs) and opt-out * early. */ - if (!_pthread_main_np()) { error("pthread_self() called from alien thread named ", - "'", myself->name().string(), "'"); - + "'", Thread::myself()->name().string(), "'"); return nullptr; } /* - * We create a pthread object containing a copy of main thread's - * Thread object. Therefore, we ensure the pthread object does not - * get deleted by allocating it in heap via new(). Otherwise, the - * static destruction of the pthread object would also destruct the - * 'Thread' of the main thread. + * We create a pthread object associated to the main thread's Thread + * object. We ensure the pthread object does never get deleted by + * allocating it in heap via new(). Otherwise, the static destruction + * of the pthread object would also destruct the 'Thread' of the main + * thread. */ - - static struct pthread_attr main_thread_attr; - static struct pthread *main = new pthread(*myself, &main_thread_attr); - + static pthread_attr main_thread_attr; + static pthread *main = new pthread(*Thread::myself(), &main_thread_attr); return main; } @@ -253,7 +251,7 @@ extern "C" { if (!attr) return EINVAL; - *attr = pthread->_attr; + *attr = pthread->attr(); return 0; } diff --git a/repos/libports/src/lib/pthread/thread.h b/repos/libports/src/lib/pthread/thread.h index db1cd9adb8..fdd982b657 100644 --- a/repos/libports/src/lib/pthread/thread.h +++ b/repos/libports/src/lib/pthread/thread.h @@ -16,6 +16,9 @@ #ifndef _INCLUDE__SRC_LIB_PTHREAD_THREAD_H_ #define _INCLUDE__SRC_LIB_PTHREAD_THREAD_H_ +/* Genode includes */ +#include + #include /* @@ -53,60 +56,142 @@ extern "C" { pthread_attr() : pthread(0), stack_size(0) { } }; - /* * This class is named 'struct pthread' because the 'pthread_t' type is * defined as 'struct pthread*' in '_pthreadtypes.h' */ - struct pthread : Genode::Thread - { - pthread_attr_t _attr; - void *(*_start_routine) (void *); - void *_arg; - - enum { WEIGHT = Genode::Cpu_session::Weight::DEFAULT_WEIGHT }; - - pthread(pthread_attr_t attr, void *(*start_routine) (void *), - void *arg, size_t stack_size, char const * name, - Genode::Cpu_session * cpu, Genode::Affinity::Location location) - : Thread(WEIGHT, name, stack_size, Type::NORMAL, cpu, location), - _attr(attr), - _start_routine(start_routine), - _arg(arg) - { - if (_attr) - _attr->pthread = this; - - pthread_registry().insert(this); - } - - /** - * Constructor to create pthread object out of existing thread, - * e.g. main Genode thread - */ - pthread(Thread &myself, pthread_attr_t attr) - : Thread(myself), - _attr(attr), _start_routine(nullptr), _arg(nullptr) - { - if (_attr) - _attr->pthread = this; - - pthread_registry().insert(this); - } - - virtual ~pthread() - { - pthread_registry().remove(this); - } - - void entry() - { - void *exit_status = _start_routine(_arg); - pthread_exit(exit_status); - } - }; + struct pthread; void pthread_cleanup(); } + +struct Genode::Thread::Tls::Base +{ + /** + * Register thread-local-storage object at Genode thread + */ + static void tls(Genode::Thread &thread, Tls::Base &tls) + { + thread._tls = Tls { &tls }; + } + + struct Undefined : Exception { }; + + /** + * Obtain thread-local-storage object for the calling thread + * + * \throw Undefined + */ + static Tls::Base &tls() + { + Thread &myself = *Thread::myself(); + + if (!myself._tls.ptr) + throw Undefined(); + + return *myself._tls.ptr; + } +}; + + +struct pthread : Genode::Noncopyable, Genode::Thread::Tls::Base +{ + typedef void *(*start_routine_t) (void *); + + private: + + struct Thread_object : Genode::Thread + { + start_routine_t _start_routine; + void *_arg; + + enum { WEIGHT = Genode::Cpu_session::Weight::DEFAULT_WEIGHT }; + + Thread_object(char const *name, size_t stack_size, + Genode::Cpu_session *cpu, + Genode::Affinity::Location location, + start_routine_t start_routine, void *arg) + : + Genode::Thread(WEIGHT, name, stack_size, Type::NORMAL, cpu, location), + _start_routine(start_routine), _arg(arg) + { } + + void entry() override + { + void *exit_status = _start_routine(_arg); + pthread_exit(exit_status); + } + }; + + Genode::Constructible _thread_object; + + /* + * Helper to construct '_thread_object' in class initializer + */ + template + Genode::Thread &_construct_thread_object(ARGS &&... args) + { + _thread_object.construct(args...); + return *_thread_object; + } + + /* + * Refers to '_thread_object' or an external 'Genode::Thread' object + */ + Genode::Thread &_thread; + + pthread_attr_t _attr; + + void _associate_thread_with_pthread() + { + if (_attr) + _attr->pthread = this; + + Genode::Thread::Tls::Base::tls(_thread, *this); + + pthread_registry().insert(this); + } + + public: + + /** + * Constructor for threads created via 'pthread_create' + */ + pthread(pthread_attr_t attr, start_routine_t start_routine, + void *arg, size_t stack_size, char const * name, + Genode::Cpu_session * cpu, Genode::Affinity::Location location) + : + _thread(_construct_thread_object(name, stack_size, cpu, location, + start_routine, arg)), + _attr(attr) + { + _associate_thread_with_pthread(); + } + + /** + * Constructor to create pthread object out of existing thread, + * i.e., the main thread + */ + pthread(Genode::Thread &existing_thread, pthread_attr_t attr) + : + _thread(existing_thread), + _attr(attr) + { + _associate_thread_with_pthread(); + } + + ~pthread() + { + pthread_registry().remove(this); + } + + void start() { _thread.start(); } + + void *stack_top() const { return _thread.stack_top(); } + void *stack_base() const { return _thread.stack_base(); } + + pthread_attr_t attr() { return _attr; } +}; + #endif /* _INCLUDE__SRC_LIB_PTHREAD_THREAD_H_ */ diff --git a/repos/ports/include/vmm/vcpu_dispatcher.h b/repos/ports/include/vmm/vcpu_dispatcher.h index 78104a913a..5673ef5962 100644 --- a/repos/ports/include/vmm/vcpu_dispatcher.h +++ b/repos/ports/include/vmm/vcpu_dispatcher.h @@ -84,22 +84,6 @@ class Vmm::Vcpu_dispatcher : public T } - template - Vcpu_dispatcher(Genode::Env &env, Genode::size_t stack_size, - Cpu_session * cpu_session, - Genode::Affinity::Location location, - X attr, void *(*start_routine) (void *), void *arg, - const char * name = "vCPU dispatcher") - : T(attr, start_routine, arg, stack_size, name, nullptr, location), - _env(env) - { - using namespace Genode; - - /* request creation of a 'local' EC */ - T::native_thread().ec_sel = Native_thread::INVALID_INDEX - 1; - T::start(); - } - /** * Register virtualization event handler */ diff --git a/repos/ports/src/virtualbox/spec/nova/sup.cc b/repos/ports/src/virtualbox/spec/nova/sup.cc index b3e83b10f3..ccbb5e9cd1 100644 --- a/repos/ports/src/virtualbox/spec/nova/sup.cc +++ b/repos/ports/src/virtualbox/spec/nova/sup.cc @@ -313,6 +313,6 @@ bool create_emt_vcpu(pthread_t * pthread, size_t stack, vcpu_handler_list().insert(vcpu_handler); - *pthread = vcpu_handler; + *pthread = &vcpu_handler->pthread_obj(); return true; } diff --git a/repos/ports/src/virtualbox/spec/nova/vcpu.h b/repos/ports/src/virtualbox/spec/nova/vcpu.h index 615e9c6ba4..c1e1c88927 100644 --- a/repos/ports/src/virtualbox/spec/nova/vcpu.h +++ b/repos/ports/src/virtualbox/spec/nova/vcpu.h @@ -74,14 +74,21 @@ extern "C" int MMIO2_MAPPED_SYNC(PVM pVM, RTGCPHYS GCPhys, size_t cbWrite, bool &writeable); -class Vcpu_handler : public Vmm::Vcpu_dispatcher, +class Vcpu_handler : public Vmm::Vcpu_dispatcher, public Genode::List::Element { + protected: + + pthread::start_routine_t const _start_routine; + void * const _start_routine_arg; + private: X86FXSTATE _guest_fpu_state __attribute__((aligned(0x10))); X86FXSTATE _emt_fpu_state __attribute__((aligned(0x10))); + pthread _pthread; + Vmm::Vcpu_other_pd _vcpu; Genode::addr_t _ec_sel; @@ -767,15 +774,19 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher, unsigned int cpu_id, const char * name, Genode::Pd_session_capability pd_vcpu) : - Vmm::Vcpu_dispatcher(env, stack_size, cpu_session, location, - attr ? *attr : 0, start_routine, arg, - name), + Vmm::Vcpu_dispatcher(env, stack_size, cpu_session, + location, name), + _pthread(*this, attr ? *attr : 0), + _start_routine(start_routine), + _start_routine_arg(arg), _vcpu(cpu_session, location, pd_vcpu), _ec_sel(Genode::cap_map()->insert()), _irq_win(false), _cpu_id(cpu_id) { } + pthread &pthread_obj() { return _pthread; } + unsigned int cpu_id() { return _cpu_id; } void start() { diff --git a/repos/ports/src/virtualbox/spec/nova/vcpu_svm.h b/repos/ports/src/virtualbox/spec/nova/vcpu_svm.h index 286f338cce..0909c368ae 100644 --- a/repos/ports/src/virtualbox/spec/nova/vcpu_svm.h +++ b/repos/ports/src/virtualbox/spec/nova/vcpu_svm.h @@ -75,7 +75,7 @@ class Vcpu_handler_svm : public Vcpu_handler | SVM_CTRL2_INTERCEPT_MONITOR | SVM_CTRL2_INTERCEPT_MWAIT; - void *exit_status = _start_routine(_arg); + void *exit_status = _start_routine(_start_routine_arg); pthread_exit(exit_status); Nova::reply(nullptr); diff --git a/repos/ports/src/virtualbox/spec/nova/vcpu_vmx.h b/repos/ports/src/virtualbox/spec/nova/vcpu_vmx.h index 7d499253c3..ec84a92a0a 100644 --- a/repos/ports/src/virtualbox/spec/nova/vcpu_vmx.h +++ b/repos/ports/src/virtualbox/spec/nova/vcpu_vmx.h @@ -83,7 +83,7 @@ class Vcpu_handler_vmx : public Vcpu_handler VMX_VMCS_CTRL_PROC_EXEC2_RDTSCP | VMX_VMCS_CTRL_PROC_EXEC2_EPT; - void *exit_status = _start_routine(_arg); + void *exit_status = _start_routine(_start_routine_arg); pthread_exit(exit_status); Nova::reply(nullptr); diff --git a/repos/ports/src/virtualbox5/spec/nova/sup.cc b/repos/ports/src/virtualbox5/spec/nova/sup.cc index 5dd07ffd89..94202c70be 100644 --- a/repos/ports/src/virtualbox5/spec/nova/sup.cc +++ b/repos/ports/src/virtualbox5/spec/nova/sup.cc @@ -799,6 +799,6 @@ bool create_emt_vcpu(pthread_t * pthread, ::size_t stack, vcpu_handler_list().insert(vcpu_handler); - *pthread = vcpu_handler; + *pthread = &vcpu_handler->pthread_obj(); return true; } diff --git a/repos/ports/src/virtualbox5/spec/nova/vcpu.h b/repos/ports/src/virtualbox5/spec/nova/vcpu.h index 8341bbe35b..8e70c18af0 100644 --- a/repos/ports/src/virtualbox5/spec/nova/vcpu.h +++ b/repos/ports/src/virtualbox5/spec/nova/vcpu.h @@ -73,14 +73,21 @@ static inline Genode::uint32_t sel_ar_conv_from_nova(Genode::uint16_t v) } -class Vcpu_handler : public Vmm::Vcpu_dispatcher, +class Vcpu_handler : public Vmm::Vcpu_dispatcher, public Genode::List::Element { + protected: + + pthread::start_routine_t const _start_routine; + void * const _start_routine_arg; + private: X86FXSTATE _guest_fpu_state __attribute__((aligned(0x10))); X86FXSTATE _emt_fpu_state __attribute__((aligned(0x10))); + pthread _pthread; + Vmm::Vcpu_other_pd _vcpu; Genode::addr_t _ec_sel; @@ -92,7 +99,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher, void fpu_save(char * data) { Assert(!(reinterpret_cast(data) & 0xF)); - asm volatile ("fxsave %0" : "=m" (*data)); + asm volatile ("fxsave %0" : "=m" (*data)); } void fpu_load(char * data) { @@ -782,20 +789,24 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher, Vcpu_handler(Genode::Env &env, size_t stack_size, const pthread_attr_t *attr, - void *(*start_routine) (void *), void *arg, + pthread::start_routine_t start_routine, void *arg, Genode::Cpu_session * cpu_session, Genode::Affinity::Location location, unsigned int cpu_id, const char * name, Genode::Pd_session_capability pd_vcpu) : - Vmm::Vcpu_dispatcher(env, stack_size, cpu_session, location, - attr ? *attr : 0, start_routine, arg, - name), + Vmm::Vcpu_dispatcher(env, stack_size, cpu_session, + location, name), + _pthread(*this, attr ? *attr : 0), + _start_routine(start_routine), + _start_routine_arg(arg), _vcpu(cpu_session, location, pd_vcpu), _ec_sel(Genode::cap_map()->insert()), _cpu_id(cpu_id) { } + pthread &pthread_obj() { return _pthread; } + unsigned int cpu_id() { return _cpu_id; } void start() { diff --git a/repos/ports/src/virtualbox5/spec/nova/vcpu_svm.h b/repos/ports/src/virtualbox5/spec/nova/vcpu_svm.h index ff38ccd2db..ae2e5d387e 100644 --- a/repos/ports/src/virtualbox5/spec/nova/vcpu_svm.h +++ b/repos/ports/src/virtualbox5/spec/nova/vcpu_svm.h @@ -71,7 +71,7 @@ class Vcpu_handler_svm : public Vcpu_handler next_utcb.ctrl[0] = SVM_CTRL1_INTERCEPT_CPUID; next_utcb.ctrl[1] = 0; - void *exit_status = _start_routine(_arg); + void *exit_status = _start_routine(_start_routine_arg); pthread_exit(exit_status); Nova::reply(nullptr); diff --git a/repos/ports/src/virtualbox5/spec/nova/vcpu_vmx.h b/repos/ports/src/virtualbox5/spec/nova/vcpu_vmx.h index f01603c415..e9ec89cee7 100644 --- a/repos/ports/src/virtualbox5/spec/nova/vcpu_vmx.h +++ b/repos/ports/src/virtualbox5/spec/nova/vcpu_vmx.h @@ -92,7 +92,7 @@ class Vcpu_handler_vmx : public Vcpu_handler VMX_VMCS_CTRL_PROC_EXEC2_RDTSCP | VMX_VMCS_CTRL_PROC_EXEC2_EPT; - void *exit_status = _start_routine(_arg); + void *exit_status = _start_routine(_start_routine_arg); pthread_exit(exit_status); Nova::reply(nullptr);