| /*++ @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.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "Host.h" | |
| #include <pthread.h> | |
| 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 | |
| }; |