]> git.proxmox.com Git - mirror_edk2.git/blob - EmulatorPkg/Unix/Host/Pthreads.c
f07bcfce66f30ef0adfa669ea568930eb949dd06
[mirror_edk2.git] / EmulatorPkg / Unix / Host / Pthreads.c
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 - 2019, 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
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
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
39 PthreadMutexTryLock (
40 IN VOID *Mutex
41 )
42 {
43 return (UINTN)pthread_mutex_trylock ((pthread_mutex_t *)Mutex);
44 }
45
46
47 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
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 *
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 ((UINTN)Start, (UINTN)Context);
113 }
114
115 UINTN
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
156 PthreadExit (
157 IN VOID *ValuePtr
158 )
159 {
160 pthread_exit (ValuePtr);
161 return;
162 }
163
164
165 UINTN
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
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
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