Signed-off-by: jljusten git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11924 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			236 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			236 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*++ @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
 | 
						|
};
 | 
						|
 | 
						|
 |