| /*++ @file |
| Since the SEC is the only program in our emulation we |
| must use a UEFI/PI mechanism to export APIs to other modules. |
| This is the role of the EFI_EMU_THUNK_PROTOCOL. |
| |
| The mUnixThunkTable exists so that a change to EFI_EMU_THUNK_PROTOCOL |
| will cause an error in initializing the array if all the member functions |
| are not added. It looks like adding a element to end and not initializing |
| it may cause the table to be initaliized with the members at the end being |
| set to zero. This is bad as jumping to zero will crash. |
| |
| Copyright (c) 2004 - 2009, Intel Corporation. All rights reserved.<BR> |
| Portions copyright (c) 2008 - 2011, Apple Inc. All rights reserved.<BR> |
| This program and the accompanying materials |
| are licensed and made available under the terms and conditions of the BSD License |
| which accompanies this distribution. The full text of the license may be found at |
| http://opensource.org/licenses/bsd-license.php |
| |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. |
| |
| **/ |
| |
| #include "Host.h" |
| |
| #ifdef __APPLE__ |
| #define DebugAssert _Mangle__DebugAssert |
| |
| #include <assert.h> |
| #include <CoreServices/CoreServices.h> |
| #include <mach/mach.h> |
| #include <mach/mach_time.h> |
| |
| #undef DebugAssert |
| #endif |
| |
| int settimer_initialized; |
| struct timeval settimer_timeval; |
| void (*settimer_callback)(UINT64 delta); |
| |
| BOOLEAN gEmulatorInterruptEnabled = FALSE; |
| |
| |
| UINTN |
| SecWriteStdErr ( |
| IN UINT8 *Buffer, |
| IN UINTN NumberOfBytes |
| ) |
| { |
| ssize_t Return; |
| |
| Return = write (STDERR_FILENO, (const void *)Buffer, (size_t)NumberOfBytes); |
| |
| return (Return == -1) ? 0 : Return; |
| } |
| |
| |
| EFI_STATUS |
| SecConfigStdIn ( |
| VOID |
| ) |
| { |
| struct termios tty; |
| |
| // |
| // Need to turn off line buffering, ECHO, and make it unbuffered. |
| // |
| tcgetattr (STDIN_FILENO, &tty); |
| tty.c_lflag &= ~(ICANON | ECHO); |
| tcsetattr (STDIN_FILENO, TCSANOW, &tty); |
| |
| // setvbuf (STDIN_FILENO, NULL, _IONBF, 0); |
| |
| // now ioctl FIONREAD will do what we need |
| return EFI_SUCCESS; |
| } |
| |
| UINTN |
| SecWriteStdOut ( |
| IN UINT8 *Buffer, |
| IN UINTN NumberOfBytes |
| ) |
| { |
| ssize_t Return; |
| |
| Return = write (STDOUT_FILENO, (const void *)Buffer, (size_t)NumberOfBytes); |
| |
| return (Return == -1) ? 0 : Return; |
| } |
| |
| UINTN |
| SecReadStdIn ( |
| IN UINT8 *Buffer, |
| IN UINTN NumberOfBytes |
| ) |
| { |
| ssize_t Return; |
| |
| Return = read (STDIN_FILENO, Buffer, (size_t)NumberOfBytes); |
| |
| return (Return == -1) ? 0 : Return; |
| } |
| |
| BOOLEAN |
| SecPollStdIn ( |
| VOID |
| ) |
| { |
| int Result; |
| int Bytes; |
| |
| Result = ioctl (STDIN_FILENO, FIONREAD, &Bytes); |
| if (Result == -1) { |
| return FALSE; |
| } |
| |
| return (BOOLEAN)(Bytes > 0); |
| } |
| |
| |
| VOID * |
| SecMalloc ( |
| IN UINTN Size |
| ) |
| { |
| return malloc ((size_t)Size); |
| } |
| |
| VOID * |
| SecValloc ( |
| IN UINTN Size |
| ) |
| { |
| return valloc ((size_t)Size); |
| } |
| |
| BOOLEAN |
| SecFree ( |
| IN VOID *Ptr |
| ) |
| { |
| if (EfiSystemMemoryRange (Ptr)) { |
| // If an address range is in the EFI memory map it was alloced via EFI. |
| // So don't free those ranges and let the caller know. |
| return FALSE; |
| } |
| |
| free (Ptr); |
| return TRUE; |
| } |
| |
| |
| void |
| settimer_handler (int sig) |
| { |
| struct timeval timeval; |
| UINT64 delta; |
| |
| gettimeofday (&timeval, NULL); |
| delta = ((UINT64)timeval.tv_sec * 1000) + (timeval.tv_usec / 1000) |
| - ((UINT64)settimer_timeval.tv_sec * 1000) |
| - (settimer_timeval.tv_usec / 1000); |
| settimer_timeval = timeval; |
| |
| if (settimer_callback) { |
| ReverseGasketUint64 (settimer_callback, delta); |
| } |
| } |
| |
| VOID |
| SecSetTimer ( |
| IN UINT64 PeriodMs, |
| IN EMU_SET_TIMER_CALLBACK CallBack |
| ) |
| { |
| struct itimerval timerval; |
| UINT32 remainder; |
| |
| if (!settimer_initialized) { |
| struct sigaction act; |
| |
| settimer_initialized = 1; |
| act.sa_handler = settimer_handler; |
| act.sa_flags = 0; |
| sigemptyset (&act.sa_mask); |
| gEmulatorInterruptEnabled = TRUE; |
| if (sigaction (SIGALRM, &act, NULL) != 0) { |
| printf ("SetTimer: sigaction error %s\n", strerror (errno)); |
| } |
| if (gettimeofday (&settimer_timeval, NULL) != 0) { |
| printf ("SetTimer: gettimeofday error %s\n", strerror (errno)); |
| } |
| } |
| timerval.it_value.tv_sec = DivU64x32(PeriodMs, 1000); |
| DivU64x32Remainder(PeriodMs, 1000, &remainder); |
| timerval.it_value.tv_usec = remainder * 1000; |
| timerval.it_value.tv_sec = DivU64x32(PeriodMs, 1000); |
| timerval.it_interval = timerval.it_value; |
| |
| if (setitimer (ITIMER_REAL, &timerval, NULL) != 0) { |
| printf ("SetTimer: setitimer error %s\n", strerror (errno)); |
| } |
| settimer_callback = CallBack; |
| } |
| |
| |
| VOID |
| SecEnableInterrupt ( |
| VOID |
| ) |
| { |
| sigset_t sigset; |
| |
| gEmulatorInterruptEnabled = TRUE; |
| // Since SetTimer() uses SIGALRM we emulate turning on and off interrupts |
| // by enabling/disabling SIGALRM. |
| sigemptyset (&sigset); |
| sigaddset (&sigset, SIGALRM); |
| pthread_sigmask (SIG_UNBLOCK, &sigset, NULL); |
| } |
| |
| |
| VOID |
| SecDisableInterrupt ( |
| VOID |
| ) |
| { |
| sigset_t sigset; |
| |
| // Since SetTimer() uses SIGALRM we emulate turning on and off interrupts |
| // by enabling/disabling SIGALRM. |
| sigemptyset (&sigset); |
| sigaddset (&sigset, SIGALRM); |
| pthread_sigmask (SIG_BLOCK, &sigset, NULL); |
| gEmulatorInterruptEnabled = FALSE; |
| } |
| |
| |
| BOOLEAN |
| SecInterruptEanbled (void) |
| { |
| return gEmulatorInterruptEnabled; |
| } |
| |
| |
| UINT64 |
| QueryPerformanceFrequency ( |
| VOID |
| ) |
| { |
| // Hard code to nanoseconds |
| return 1000000000ULL; |
| } |
| |
| UINT64 |
| QueryPerformanceCounter ( |
| VOID |
| ) |
| { |
| #if __APPLE__ |
| UINT64 Start; |
| static mach_timebase_info_data_t sTimebaseInfo; |
| |
| |
| Start = mach_absolute_time (); |
| |
| // Convert to nanoseconds. |
| |
| // If this is the first time we've run, get the timebase. |
| // We can use denom == 0 to indicate that sTimebaseInfo is |
| // uninitialised because it makes no sense to have a zero |
| // denominator is a fraction. |
| |
| if ( sTimebaseInfo.denom == 0 ) { |
| (void) mach_timebase_info(&sTimebaseInfo); |
| } |
| |
| // Do the maths. We hope that the multiplication doesn't |
| // overflow; the price you pay for working in fixed point. |
| |
| return (Start * sTimebaseInfo.numer) / sTimebaseInfo.denom; |
| #else |
| // Need to figure out what to do for Linux? |
| return 0; |
| #endif |
| } |
| |
| |
| |
| VOID |
| SecSleep ( |
| IN UINT64 Nanoseconds |
| ) |
| { |
| struct timespec rq, rm; |
| struct timeval start, end; |
| unsigned long MicroSec; |
| |
| rq.tv_sec = DivU64x32 (Nanoseconds, 1000000000); |
| rq.tv_nsec = ModU64x32 (Nanoseconds, 1000000000); |
| |
| // |
| // nanosleep gets interrupted by our timer tic. |
| // we need to track wall clock time or we will stall for way too long |
| // |
| gettimeofday (&start, NULL); |
| end.tv_sec = start.tv_sec + rq.tv_sec; |
| MicroSec = (start.tv_usec + rq.tv_nsec/1000); |
| end.tv_usec = MicroSec % 1000000; |
| if (MicroSec > 1000000) { |
| end.tv_sec++; |
| } |
| |
| while (nanosleep (&rq, &rm) == -1) { |
| if (errno != EINTR) { |
| break; |
| } |
| gettimeofday (&start, NULL); |
| if (start.tv_sec > end.tv_sec) { |
| break; |
| } if ((start.tv_sec == end.tv_sec) && (start.tv_usec > end.tv_usec)) { |
| break; |
| } |
| rq = rm; |
| } |
| } |
| |
| |
| VOID |
| SecCpuSleep ( |
| VOID |
| ) |
| { |
| struct timespec rq, rm; |
| |
| // nanosleep gets interrupted by the timer tic |
| rq.tv_sec = 1; |
| rq.tv_nsec = 0; |
| |
| nanosleep (&rq, &rm); |
| } |
| |
| |
| VOID |
| SecExit ( |
| UINTN Status |
| ) |
| { |
| exit (Status); |
| } |
| |
| |
| VOID |
| SecGetTime ( |
| OUT EFI_TIME *Time, |
| OUT EFI_TIME_CAPABILITIES *Capabilities OPTIONAL |
| ) |
| { |
| struct tm *tm; |
| time_t t; |
| |
| t = time (NULL); |
| tm = localtime (&t); |
| |
| Time->Year = 1900 + tm->tm_year; |
| Time->Month = tm->tm_mon + 1; |
| Time->Day = tm->tm_mday; |
| Time->Hour = tm->tm_hour; |
| Time->Minute = tm->tm_min; |
| Time->Second = tm->tm_sec; |
| Time->Nanosecond = 0; |
| Time->TimeZone = timezone; |
| Time->Daylight = (daylight ? EFI_TIME_ADJUST_DAYLIGHT : 0) |
| | (tm->tm_isdst > 0 ? EFI_TIME_IN_DAYLIGHT : 0); |
| |
| if (Capabilities != NULL) { |
| Capabilities->Resolution = 1; |
| Capabilities->Accuracy = 50000000; |
| Capabilities->SetsToZero = FALSE; |
| } |
| } |
| |
| |
| |
| VOID |
| SecSetTime ( |
| IN EFI_TIME *Time |
| ) |
| { |
| // Don't change the time on the system |
| // We could save delta to localtime() and have SecGetTime adjust return values? |
| return; |
| } |
| |
| |
| EFI_STATUS |
| SecGetNextProtocol ( |
| IN BOOLEAN EmuBusDriver, |
| OUT EMU_IO_THUNK_PROTOCOL **Instance OPTIONAL |
| ) |
| { |
| return GetNextThunkProtocol (EmuBusDriver, Instance); |
| } |
| |
| |
| EMU_THUNK_PROTOCOL gEmuThunkProtocol = { |
| GasketSecWriteStdErr, |
| GasketSecConfigStdIn, |
| GasketSecWriteStdOut, |
| GasketSecReadStdIn, |
| GasketSecPollStdIn, |
| GasketSecMalloc, |
| GasketSecValloc, |
| GasketSecFree, |
| GasketSecPeCoffGetEntryPoint, |
| GasketSecPeCoffRelocateImageExtraAction, |
| GasketSecPeCoffUnloadImageExtraAction, |
| GasketSecEnableInterrupt, |
| GasketSecDisableInterrupt, |
| GasketQueryPerformanceFrequency, |
| GasketQueryPerformanceCounter, |
| GasketSecSleep, |
| GasketSecCpuSleep, |
| GasketSecExit, |
| GasketSecGetTime, |
| GasketSecSetTime, |
| GasketSecSetTimer, |
| GasketSecGetNextProtocol |
| }; |
| |
| |
| VOID |
| SecInitThunkProtocol ( |
| VOID |
| ) |
| { |
| // timezone and daylight lib globals depend on tzset be called 1st. |
| tzset (); |
| } |
| |