mirror of https://github.com/acidanthera/audk.git
230 lines
4.3 KiB
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
|
|
};
|
|
|
|
|