| /*++ @file |
| POSIX Pthreads to emulate APs and implement threads |
| |
| Copyright (c) 2011, Apple Inc. All rights reserved. |
| Copyright (c) 2011, Intel Corporation. All rights reserved.<BR> |
| |
| This program and the accompanying materials |
| are licensed and made available under the terms and conditions of the BSD License |
| which accompanies this distribution. The full text of the license may be found at |
| http://opensource.org/licenses/bsd-license.php |
| |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. |
| |
| |
| **/ |
| |
| #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 ((CALL_BACK)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; |
| } |
| |
| // Aquire 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 |
| }; |
| |
| |