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