audk/EmulatorPkg/Unix/Host/Pthreads.c

230 lines
4.3 KiB
C

/*++ @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
};