/*++ @file POSIX Pthreads to emulate APs and implement threads Copyright (c) 2011, Apple Inc. All rights reserved. Copyright (c) 2011 - 2019, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "Host.h" #include UINTN EFIAPI PthreadMutexLock ( IN VOID *Mutex ) { return (UINTN)pthread_mutex_lock ((pthread_mutex_t *)Mutex); } UINTN EFIAPI PthreadMutexUnLock ( IN VOID *Mutex ) { return (UINTN)pthread_mutex_unlock ((pthread_mutex_t *)Mutex); } UINTN EFIAPI PthreadMutexTryLock ( IN VOID *Mutex ) { return (UINTN)pthread_mutex_trylock ((pthread_mutex_t *)Mutex); } VOID * PthreadMutexInit ( IN VOID ) { pthread_mutex_t *Mutex; int err; Mutex = malloc (sizeof (pthread_mutex_t)); err = pthread_mutex_init (Mutex, NULL); if (err == 0) { return Mutex; } return NULL; } UINTN PthreadMutexDestroy ( IN VOID *Mutex ) { if (Mutex != NULL) { return pthread_mutex_destroy ((pthread_mutex_t *)Mutex); } return -1; } // Can't store this data on PthreadCreate stack so we need a global typedef struct { pthread_mutex_t Mutex; THREAD_THUNK_THREAD_ENTRY Start; } THREAD_MANGLE; THREAD_MANGLE mThreadMangle = { PTHREAD_MUTEX_INITIALIZER, NULL }; VOID * SecFakePthreadStart ( VOID *Context ) { THREAD_THUNK_THREAD_ENTRY Start; sigset_t SigMask; // Save global on the stack before we unlock Start = mThreadMangle.Start; pthread_mutex_unlock (&mThreadMangle.Mutex); // Mask all signals to the APs sigfillset (&SigMask); pthread_sigmask (SIG_BLOCK, &SigMask, NULL); // // We have to start the thread in SEC as we need to follow // OS X calling conventions. We can then call back into // to the callers Start. // // This is a great example of how all problems in computer // science can be solved by adding another level of indirection // return (VOID *)ReverseGasketUint64 ((UINTN)Start, (UINTN)Context); } UINTN PthreadCreate ( IN VOID *Thread, IN VOID *Attribute, IN THREAD_THUNK_THREAD_ENTRY Start, IN VOID *Context ) { int err; BOOLEAN EnabledOnEntry; // // Threads inherit interrupt state so disable interrupts before we start thread // if (SecInterruptEanbled ()) { SecDisableInterrupt (); EnabledOnEntry = TRUE; } else { EnabledOnEntry = FALSE; } // Acquire lock for global, SecFakePthreadStart runs in a different thread. pthread_mutex_lock (&mThreadMangle.Mutex); mThreadMangle.Start = Start; err = pthread_create (Thread, Attribute, SecFakePthreadStart, Context); if (err != 0) { // Thread failed to launch so release the lock; pthread_mutex_unlock (&mThreadMangle.Mutex); } if (EnabledOnEntry) { // Restore interrupt state SecEnableInterrupt (); } return err; } VOID PthreadExit ( IN VOID *ValuePtr ) { pthread_exit (ValuePtr); return; } UINTN PthreadSelf ( VOID ) { // POSIX currently allows pthread_t to be a structure or arithmetic type. // Check out sys/types.h to make sure this will work if you are porting. // On OS X (Darwin) pthread_t is a pointer to a structure so this code works. return (UINTN)pthread_self (); } EMU_THREAD_THUNK_PROTOCOL gPthreadThunk = { GasketPthreadMutexLock, GasketPthreadMutexUnLock, GasketPthreadMutexTryLock, GasketPthreadMutexInit, GasketPthreadMutexDestroy, GasketPthreadCreate, GasketPthreadExit, GasketPthreadSelf }; EFI_STATUS PthreadOpen ( IN EMU_IO_THUNK_PROTOCOL *This ) { if (This->Instance != 0) { // Only single instance is supported return EFI_NOT_FOUND; } if (This->ConfigString[0] == L'0') { // If AP count is zero no need for threads return EFI_NOT_FOUND; } This->Interface = &gPthreadThunk; return EFI_SUCCESS; } EFI_STATUS PthreadClose ( IN EMU_IO_THUNK_PROTOCOL *This ) { return EFI_SUCCESS; } EMU_IO_THUNK_PROTOCOL gPthreadThunkIo = { &gEmuThreadThunkProtocolGuid, NULL, NULL, 0, GasketPthreadOpen, GasketPthreadClose, NULL };