git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@14016 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			441 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			441 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*++ @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 ();
 | 
						|
}
 | 
						|
 |