mirror of https://github.com/acidanthera/audk.git
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 ();
|
|
}
|
|
|