]> git.proxmox.com Git - mirror_edk2.git/blob - EmulatorPkg/Unix/Host/EmuThunk.c
1e9dc99187dbced7555ae801b0b7ec8d7f953370
[mirror_edk2.git] / EmulatorPkg / Unix / Host / EmuThunk.c
1 /*++ @file
2 Since the SEC is the only program in our emulation we
3 must use a UEFI/PI mechanism to export APIs to other modules.
4 This is the role of the EFI_EMU_THUNK_PROTOCOL.
5
6 The mUnixThunkTable exists so that a change to EFI_EMU_THUNK_PROTOCOL
7 will cause an error in initializing the array if all the member functions
8 are not added. It looks like adding a element to end and not initializing
9 it may cause the table to be initaliized with the members at the end being
10 set to zero. This is bad as jumping to zero will crash.
11
12 Copyright (c) 2004 - 2019, Intel Corporation. All rights reserved.<BR>
13 Portions copyright (c) 2008 - 2011, Apple Inc. All rights reserved.<BR>
14 SPDX-License-Identifier: BSD-2-Clause-Patent
15
16 **/
17
18 #include "Host.h"
19
20 #ifdef __APPLE__
21 #define DebugAssert _Mangle__DebugAssert
22
23 #include <assert.h>
24 #include <CoreServices/CoreServices.h>
25 #include <mach/mach.h>
26 #include <mach/mach_time.h>
27
28 #undef DebugAssert
29 #endif
30
31 int settimer_initialized;
32 struct timeval settimer_timeval;
33 UINTN settimer_callback = 0;
34
35 BOOLEAN gEmulatorInterruptEnabled = FALSE;
36
37
38 UINTN
39 SecWriteStdErr (
40 IN UINT8 *Buffer,
41 IN UINTN NumberOfBytes
42 )
43 {
44 ssize_t Return;
45
46 Return = write (STDERR_FILENO, (const void *)Buffer, (size_t)NumberOfBytes);
47
48 return (Return == -1) ? 0 : Return;
49 }
50
51
52 EFI_STATUS
53 SecConfigStdIn (
54 VOID
55 )
56 {
57 struct termios tty;
58
59 //
60 // Need to turn off line buffering, ECHO, and make it unbuffered.
61 //
62 tcgetattr (STDIN_FILENO, &tty);
63 tty.c_lflag &= ~(ICANON | ECHO);
64 tcsetattr (STDIN_FILENO, TCSANOW, &tty);
65
66 // setvbuf (STDIN_FILENO, NULL, _IONBF, 0);
67
68 // now ioctl FIONREAD will do what we need
69 return EFI_SUCCESS;
70 }
71
72 UINTN
73 SecWriteStdOut (
74 IN UINT8 *Buffer,
75 IN UINTN NumberOfBytes
76 )
77 {
78 ssize_t Return;
79
80 Return = write (STDOUT_FILENO, (const void *)Buffer, (size_t)NumberOfBytes);
81
82 return (Return == -1) ? 0 : Return;
83 }
84
85 UINTN
86 SecReadStdIn (
87 IN UINT8 *Buffer,
88 IN UINTN NumberOfBytes
89 )
90 {
91 ssize_t Return;
92
93 Return = read (STDIN_FILENO, Buffer, (size_t)NumberOfBytes);
94
95 return (Return == -1) ? 0 : Return;
96 }
97
98 BOOLEAN
99 SecPollStdIn (
100 VOID
101 )
102 {
103 int Result;
104 int Bytes;
105
106 Result = ioctl (STDIN_FILENO, FIONREAD, &Bytes);
107 if (Result == -1) {
108 return FALSE;
109 }
110
111 return (BOOLEAN)(Bytes > 0);
112 }
113
114
115 VOID *
116 SecMalloc (
117 IN UINTN Size
118 )
119 {
120 return malloc ((size_t)Size);
121 }
122
123 VOID *
124 SecValloc (
125 IN UINTN Size
126 )
127 {
128 return valloc ((size_t)Size);
129 }
130
131 BOOLEAN
132 SecFree (
133 IN VOID *Ptr
134 )
135 {
136 if (EfiSystemMemoryRange (Ptr)) {
137 // If an address range is in the EFI memory map it was alloced via EFI.
138 // So don't free those ranges and let the caller know.
139 return FALSE;
140 }
141
142 free (Ptr);
143 return TRUE;
144 }
145
146
147 void
148 settimer_handler (int sig)
149 {
150 struct timeval timeval;
151 UINT64 delta;
152
153 gettimeofday (&timeval, NULL);
154 delta = ((UINT64)timeval.tv_sec * 1000) + (timeval.tv_usec / 1000)
155 - ((UINT64)settimer_timeval.tv_sec * 1000)
156 - (settimer_timeval.tv_usec / 1000);
157 settimer_timeval = timeval;
158
159 if (settimer_callback) {
160 ReverseGasketUint64 (settimer_callback, delta);
161 }
162 }
163
164 VOID
165 SecSetTimer (
166 IN UINT64 PeriodMs,
167 IN EMU_SET_TIMER_CALLBACK CallBack
168 )
169 {
170 struct itimerval timerval;
171 UINT32 remainder;
172
173 if (!settimer_initialized) {
174 struct sigaction act;
175
176 settimer_initialized = 1;
177 act.sa_handler = settimer_handler;
178 act.sa_flags = 0;
179 sigemptyset (&act.sa_mask);
180 gEmulatorInterruptEnabled = TRUE;
181 if (sigaction (SIGALRM, &act, NULL) != 0) {
182 printf ("SetTimer: sigaction error %s\n", strerror (errno));
183 }
184 if (gettimeofday (&settimer_timeval, NULL) != 0) {
185 printf ("SetTimer: gettimeofday error %s\n", strerror (errno));
186 }
187 }
188 timerval.it_value.tv_sec = DivU64x32(PeriodMs, 1000);
189 DivU64x32Remainder(PeriodMs, 1000, &remainder);
190 timerval.it_value.tv_usec = remainder * 1000;
191 timerval.it_value.tv_sec = DivU64x32(PeriodMs, 1000);
192 timerval.it_interval = timerval.it_value;
193
194 if (setitimer (ITIMER_REAL, &timerval, NULL) != 0) {
195 printf ("SetTimer: setitimer error %s\n", strerror (errno));
196 }
197 settimer_callback = (UINTN)CallBack;
198 }
199
200
201 VOID
202 SecEnableInterrupt (
203 VOID
204 )
205 {
206 sigset_t sigset;
207
208 gEmulatorInterruptEnabled = TRUE;
209 // Since SetTimer() uses SIGALRM we emulate turning on and off interrupts
210 // by enabling/disabling SIGALRM.
211 sigemptyset (&sigset);
212 sigaddset (&sigset, SIGALRM);
213 pthread_sigmask (SIG_UNBLOCK, &sigset, NULL);
214 }
215
216
217 VOID
218 SecDisableInterrupt (
219 VOID
220 )
221 {
222 sigset_t sigset;
223
224 // Since SetTimer() uses SIGALRM we emulate turning on and off interrupts
225 // by enabling/disabling SIGALRM.
226 sigemptyset (&sigset);
227 sigaddset (&sigset, SIGALRM);
228 pthread_sigmask (SIG_BLOCK, &sigset, NULL);
229 gEmulatorInterruptEnabled = FALSE;
230 }
231
232
233 BOOLEAN
234 SecInterruptEanbled (void)
235 {
236 return gEmulatorInterruptEnabled;
237 }
238
239
240 UINT64
241 QueryPerformanceFrequency (
242 VOID
243 )
244 {
245 // Hard code to nanoseconds
246 return 1000000000ULL;
247 }
248
249 UINT64
250 QueryPerformanceCounter (
251 VOID
252 )
253 {
254 #if __APPLE__
255 UINT64 Start;
256 static mach_timebase_info_data_t sTimebaseInfo;
257
258
259 Start = mach_absolute_time ();
260
261 // Convert to nanoseconds.
262
263 // If this is the first time we've run, get the timebase.
264 // We can use denom == 0 to indicate that sTimebaseInfo is
265 // uninitialised because it makes no sense to have a zero
266 // denominator is a fraction.
267
268 if ( sTimebaseInfo.denom == 0 ) {
269 (void) mach_timebase_info(&sTimebaseInfo);
270 }
271
272 // Do the maths. We hope that the multiplication doesn't
273 // overflow; the price you pay for working in fixed point.
274
275 return (Start * sTimebaseInfo.numer) / sTimebaseInfo.denom;
276 #else
277 // Need to figure out what to do for Linux?
278 return 0;
279 #endif
280 }
281
282
283
284 VOID
285 SecSleep (
286 IN UINT64 Nanoseconds
287 )
288 {
289 struct timespec rq, rm;
290 struct timeval start, end;
291 unsigned long MicroSec;
292
293 rq.tv_sec = DivU64x32 (Nanoseconds, 1000000000);
294 rq.tv_nsec = ModU64x32 (Nanoseconds, 1000000000);
295
296 //
297 // nanosleep gets interrupted by our timer tic.
298 // we need to track wall clock time or we will stall for way too long
299 //
300 gettimeofday (&start, NULL);
301 end.tv_sec = start.tv_sec + rq.tv_sec;
302 MicroSec = (start.tv_usec + rq.tv_nsec/1000);
303 end.tv_usec = MicroSec % 1000000;
304 if (MicroSec > 1000000) {
305 end.tv_sec++;
306 }
307
308 while (nanosleep (&rq, &rm) == -1) {
309 if (errno != EINTR) {
310 break;
311 }
312 gettimeofday (&start, NULL);
313 if (start.tv_sec > end.tv_sec) {
314 break;
315 } if ((start.tv_sec == end.tv_sec) && (start.tv_usec > end.tv_usec)) {
316 break;
317 }
318 rq = rm;
319 }
320 }
321
322
323 VOID
324 SecCpuSleep (
325 VOID
326 )
327 {
328 struct timespec rq, rm;
329
330 // nanosleep gets interrupted by the timer tic
331 rq.tv_sec = 1;
332 rq.tv_nsec = 0;
333
334 nanosleep (&rq, &rm);
335 }
336
337
338 VOID
339 SecExit (
340 UINTN Status
341 )
342 {
343 exit (Status);
344 }
345
346
347 VOID
348 SecGetTime (
349 OUT EFI_TIME *Time,
350 OUT EFI_TIME_CAPABILITIES *Capabilities OPTIONAL
351 )
352 {
353 struct tm *tm;
354 time_t t;
355
356 t = time (NULL);
357 tm = localtime (&t);
358
359 Time->Year = 1900 + tm->tm_year;
360 Time->Month = tm->tm_mon + 1;
361 Time->Day = tm->tm_mday;
362 Time->Hour = tm->tm_hour;
363 Time->Minute = tm->tm_min;
364 Time->Second = tm->tm_sec;
365 Time->Nanosecond = 0;
366 Time->TimeZone = timezone / 60;
367 Time->Daylight = (daylight ? EFI_TIME_ADJUST_DAYLIGHT : 0)
368 | (tm->tm_isdst > 0 ? EFI_TIME_IN_DAYLIGHT : 0);
369
370 if (Capabilities != NULL) {
371 Capabilities->Resolution = 1;
372 Capabilities->Accuracy = 50000000;
373 Capabilities->SetsToZero = FALSE;
374 }
375 }
376
377
378
379 VOID
380 SecSetTime (
381 IN EFI_TIME *Time
382 )
383 {
384 // Don't change the time on the system
385 // We could save delta to localtime() and have SecGetTime adjust return values?
386 return;
387 }
388
389
390 EFI_STATUS
391 SecGetNextProtocol (
392 IN BOOLEAN EmuBusDriver,
393 OUT EMU_IO_THUNK_PROTOCOL **Instance OPTIONAL
394 )
395 {
396 return GetNextThunkProtocol (EmuBusDriver, Instance);
397 }
398
399
400 EMU_THUNK_PROTOCOL gEmuThunkProtocol = {
401 GasketSecWriteStdErr,
402 GasketSecConfigStdIn,
403 GasketSecWriteStdOut,
404 GasketSecReadStdIn,
405 GasketSecPollStdIn,
406 GasketSecMalloc,
407 GasketSecValloc,
408 GasketSecFree,
409 GasketSecPeCoffGetEntryPoint,
410 GasketSecPeCoffRelocateImageExtraAction,
411 GasketSecPeCoffUnloadImageExtraAction,
412 GasketSecEnableInterrupt,
413 GasketSecDisableInterrupt,
414 GasketQueryPerformanceFrequency,
415 GasketQueryPerformanceCounter,
416 GasketSecSleep,
417 GasketSecCpuSleep,
418 GasketSecExit,
419 GasketSecGetTime,
420 GasketSecSetTime,
421 GasketSecSetTimer,
422 GasketSecGetNextProtocol
423 };
424
425
426 VOID
427 SecInitThunkProtocol (
428 VOID
429 )
430 {
431 // timezone and daylight lib globals depend on tzset be called 1st.
432 tzset ();
433 }
434