1 /*++ @file
2  POSIX Pthreads to emulate APs and implement threads
3 
4 Copyright (c) 2011, Apple Inc. All rights reserved.
5 Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>
6 
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8 
9 
10 **/
11 
12 #include "Host.h"
13 #include <pthread.h>
14 
15 
16 UINTN
17 EFIAPI
PthreadMutexLock(IN VOID * Mutex)18 PthreadMutexLock (
19   IN VOID *Mutex
20   )
21 {
22   return (UINTN)pthread_mutex_lock ((pthread_mutex_t *)Mutex);
23 }
24 
25 
26 
27 UINTN
28 EFIAPI
PthreadMutexUnLock(IN VOID * Mutex)29 PthreadMutexUnLock (
30   IN VOID *Mutex
31   )
32 {
33   return (UINTN)pthread_mutex_unlock ((pthread_mutex_t *)Mutex);
34 }
35 
36 
37 UINTN
38 EFIAPI
PthreadMutexTryLock(IN VOID * Mutex)39 PthreadMutexTryLock (
40   IN VOID *Mutex
41   )
42 {
43   return (UINTN)pthread_mutex_trylock ((pthread_mutex_t *)Mutex);
44 }
45 
46 
47 VOID *
PthreadMutexInit(IN VOID)48 PthreadMutexInit (
49   IN VOID
50   )
51 {
52   pthread_mutex_t *Mutex;
53   int             err;
54 
55   Mutex = malloc (sizeof (pthread_mutex_t));
56   err = pthread_mutex_init (Mutex, NULL);
57   if (err == 0) {
58     return Mutex;
59   }
60 
61   return NULL;
62 }
63 
64 
65 UINTN
PthreadMutexDestroy(IN VOID * Mutex)66 PthreadMutexDestroy (
67   IN VOID *Mutex
68   )
69 {
70   if (Mutex != NULL) {
71     return pthread_mutex_destroy ((pthread_mutex_t *)Mutex);
72   }
73 
74   return -1;
75 }
76 
77 // Can't store this data on PthreadCreate stack so we need a global
78 typedef struct {
79   pthread_mutex_t             Mutex;
80   THREAD_THUNK_THREAD_ENTRY   Start;
81 } THREAD_MANGLE;
82 
83 THREAD_MANGLE mThreadMangle = {
84   PTHREAD_MUTEX_INITIALIZER,
85   NULL
86 };
87 
88 VOID *
SecFakePthreadStart(VOID * Context)89 SecFakePthreadStart (
90   VOID  *Context
91   )
92 {
93   THREAD_THUNK_THREAD_ENTRY Start;
94   sigset_t                  SigMask;
95 
96   // Save global on the stack before we unlock
97   Start   = mThreadMangle.Start;
98   pthread_mutex_unlock (&mThreadMangle.Mutex);
99 
100   // Mask all signals to the APs
101   sigfillset (&SigMask);
102   pthread_sigmask (SIG_BLOCK, &SigMask, NULL);
103 
104   //
105   // We have to start the thread in SEC as we need to follow
106   // OS X calling conventions. We can then call back into
107   // to the callers Start.
108   //
109   // This is a great example of how all problems in computer
110   // science can be solved by adding another level of indirection
111   //
112  return  (VOID *)ReverseGasketUint64 ((CALL_BACK)Start, (UINTN)Context);
113 }
114 
115 UINTN
PthreadCreate(IN VOID * Thread,IN VOID * Attribute,IN THREAD_THUNK_THREAD_ENTRY Start,IN VOID * Context)116 PthreadCreate (
117   IN  VOID                      *Thread,
118   IN  VOID                      *Attribute,
119   IN  THREAD_THUNK_THREAD_ENTRY Start,
120   IN  VOID                      *Context
121   )
122 {
123   int         err;
124   BOOLEAN     EnabledOnEntry;
125 
126   //
127   // Threads inherit interrupt state so disable interrupts before we start thread
128   //
129   if (SecInterruptEanbled ()) {
130     SecDisableInterrupt ();
131     EnabledOnEntry = TRUE;
132   } else {
133     EnabledOnEntry = FALSE;
134   }
135 
136   // Aquire lock for global, SecFakePthreadStart runs in a different thread.
137   pthread_mutex_lock (&mThreadMangle.Mutex);
138   mThreadMangle.Start   = Start;
139 
140   err = pthread_create (Thread, Attribute, SecFakePthreadStart, Context);
141   if (err != 0) {
142     // Thread failed to launch so release the lock;
143     pthread_mutex_unlock (&mThreadMangle.Mutex);
144   }
145 
146   if (EnabledOnEntry) {
147     // Restore interrupt state
148     SecEnableInterrupt ();
149   }
150 
151   return err;
152 }
153 
154 
155 VOID
PthreadExit(IN VOID * ValuePtr)156 PthreadExit (
157   IN VOID *ValuePtr
158   )
159 {
160   pthread_exit (ValuePtr);
161   return;
162 }
163 
164 
165 UINTN
PthreadSelf(VOID)166 PthreadSelf (
167   VOID
168   )
169 {
170   // POSIX currently allows pthread_t to be a structure or arithmetic type.
171   // Check out sys/types.h to make sure this will work if you are porting.
172   // On OS X (Darwin) pthread_t is a pointer to a structure so this code works.
173   return (UINTN)pthread_self ();
174 }
175 
176 
177 EMU_THREAD_THUNK_PROTOCOL gPthreadThunk = {
178   GasketPthreadMutexLock,
179   GasketPthreadMutexUnLock,
180   GasketPthreadMutexTryLock,
181   GasketPthreadMutexInit,
182   GasketPthreadMutexDestroy,
183   GasketPthreadCreate,
184   GasketPthreadExit,
185   GasketPthreadSelf
186 };
187 
188 
189 EFI_STATUS
PthreadOpen(IN EMU_IO_THUNK_PROTOCOL * This)190 PthreadOpen (
191   IN  EMU_IO_THUNK_PROTOCOL   *This
192   )
193 {
194   if (This->Instance != 0) {
195     // Only single instance is supported
196     return EFI_NOT_FOUND;
197   }
198 
199   if (This->ConfigString[0] == L'0') {
200     // If AP count is zero no need for threads
201     return EFI_NOT_FOUND;
202   }
203 
204   This->Interface = &gPthreadThunk;
205 
206   return EFI_SUCCESS;
207 }
208 
209 
210 EFI_STATUS
PthreadClose(IN EMU_IO_THUNK_PROTOCOL * This)211 PthreadClose (
212   IN  EMU_IO_THUNK_PROTOCOL   *This
213   )
214 {
215   return EFI_SUCCESS;
216 }
217 
218 
219 EMU_IO_THUNK_PROTOCOL gPthreadThunkIo = {
220   &gEmuThreadThunkProtocolGuid,
221   NULL,
222   NULL,
223   0,
224   GasketPthreadOpen,
225   GasketPthreadClose,
226   NULL
227 };
228 
229 
230