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