From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f65.google.com (mail-wm1-f65.google.com [209.85.128.65]) by mx.groups.io with SMTP id smtpd.web12.11883.1589462054377178600 for ; Thu, 14 May 2020 06:14:14 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@akeo-ie.20150623.gappssmtp.com header.s=20150623 header.b=js9m1DmL; spf=none, err=permanent DNS error (domain: akeo.ie, ip: 209.85.128.65, mailfrom: pete@akeo.ie) Received: by mail-wm1-f65.google.com with SMTP id z72so22995940wmc.2 for ; Thu, 14 May 2020 06:14:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=akeo-ie.20150623.gappssmtp.com; s=20150623; h=subject:to:references:from:message-id:date:user-agent:mime-version :in-reply-to:content-language:content-transfer-encoding; bh=Xfz9OGGfZaLBDXgUS8/mSSTAM/KcZQvBsnQpI4AFSRA=; b=js9m1DmL9KgD9y6juCadcHqZN50gsMXBvsDtedYeYjafUapQ1+7gxHriy/Zeuw/uS/ faoxtlT5S0BtK14CfO1iZvc2XHiqXAES1/59myb8E1HXpvYlmK5nZJtQLrtH6I72Gppi Uu4sVYAp+qCfdDxt1tkOEsGNHAawp0Qv9qe+vrdJRbXFYJO2hUwIausCSPURiOci90WO WwEDheegCLV/JZx4jr6/XBTkTYmLBOTjGdcOci6HG54Rg0H6dA+wl2PsEpSGGVXAE16e 4AdOYpAbssw6CNK93eAStWPIuJi1LLPoC0RVFpiwcIDQBsRXEuX1WhAN0fHxBCpoDZMb ff1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-language :content-transfer-encoding; bh=Xfz9OGGfZaLBDXgUS8/mSSTAM/KcZQvBsnQpI4AFSRA=; b=a1XDye7ab6gzo+A7xa9ZPkF4DASpKM6h1vGIHmS2sO2clwuDFz3oE6zUcu0BZ8tAgs SfCSyk6g8As5i8NiijRMa9TMXAojyL3UzPjz2FMGuiSPmzY7viFhFm+hjl/BpD6y2CKp YRSQXnJrRaOr5uUVIMadM8ufnYhOPq5QC+1/8MOnV2QNqH3ee/ys7cJmyzds6ZgvGMKB sDbAq+KXNb3rGkgHPN/BZbQDOFNlP8xjIMDS33ROKzgetnxE1wMxgaTTwjXiG+XiufcN lSRAOAetyneuJNJYNiqtXj4SYphMwpoFE4Aa8h/woZFwTJlO2ruqtmOC1DK+s1jfBEG2 AeMA== X-Gm-Message-State: AGi0PubpjkJhmzR/nLmGnTWQYq/wusC6HMVp566+q74Z3f2KEkC3gsUH a6X20nQxiq+NHmJp3KXpkaqMgECK1z0= X-Google-Smtp-Source: APiQypKTdY3F51zu/qVtVp9DHknXA/SPAvkAhbI2i9w89ldiclbiEFdG8EW84fPaM01Ey3pIcUjGag== X-Received: by 2002:a1c:3884:: with SMTP id f126mr51039842wma.91.1589462052431; Thu, 14 May 2020 06:14:12 -0700 (PDT) Return-Path: Received: from [10.0.0.122] ([84.203.42.97]) by smtp.googlemail.com with ESMTPSA id c128sm41851901wma.42.2020.05.14.06.14.11 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 14 May 2020 06:14:11 -0700 (PDT) Subject: Re: [edk2] [PATCH 1/1] EmbeddedPkg/Library: Add VirtualRealTimeClockLib To: =?UTF-8?Q?Philippe_Mathieu-Daud=c3=a9?= , "devel@edk2.groups.io" References: <20190204124736.124-1-pete@akeo.ie> <20190204124736.124-2-pete@akeo.ie> <28add097-97a1-1cb0-4dbb-025906c5836b@redhat.com> From: "Pete Batard" Message-ID: <9f10aa87-5102-a293-b34e-386e43faa518@akeo.ie> Date: Thu, 14 May 2020 14:14:10 +0100 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:68.0) Gecko/20100101 Thunderbird/68.8.0 MIME-Version: 1.0 In-Reply-To: <28add097-97a1-1cb0-4dbb-025906c5836b@redhat.com> Content-Type: text/plain; charset=utf-8; format=flowed Content-Language: en-GB Content-Transfer-Encoding: 8bit Hi Phil, Not sure why this old 2019 patch suddenly appeared in your review inbox, but this was integrated last year as https://github.com/tianocore/edk2/commit/64a17fadcb79e2ce40524abb88a6863f47cbc0c7 Regards, /Pete On 2020.05.14 08:45, Philippe Mathieu-Daudé wrote: > Hi Pete, > > On 2/4/19 1:47 PM, Pete Batard wrote: >> This is designed to be used on platforms where a a real RTC is not >> available and relies on an RtcEpochSeconds variable having been set or, >> if that is not the case, falls back to using the epoch embedded at >> compilation time. >> >> Note that, in order to keep things simple for the setting of the >> compilation time variable, only GCC environments with UNIX-like shells >> and where a 'date' command is available are meant to be supported for >> now. >> >> Contributed-under: TianoCore Contribution Agreement 1.1 >> Signed-off-by: Pete Batard >> --- >> >> EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.c   | >> 400 ++++++++++++++++++++ >> >> EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.inf | >> 43 +++ >>   2 files changed, 443 insertions(+) >> >> diff --git >> a/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.c b/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.c >> >> new file mode 100644 >> index 000000000000..4c354730d02b >> --- /dev/null >> +++ >> b/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.c >> @@ -0,0 +1,400 @@ >> +/** @file >> + * >> + *  Implement virtual EFI RealTimeClock runtime services. >> + * >> + *  Coypright (c) 2019, Pete Batard >> + *  Copyright (c) 2018, Andrei Warkentin >> + *  Copyright (c) 2011-2014, ARM Ltd. All rights reserved. >> + *  Copyright (c) 2008-2010, Apple Inc. All rights reserved. >> + *  Copyright (c) Microsoft Corporation. All rights reserved. > > You forgot the year, it should be 2019 right? > Another occurrence below. > >> + * >> + *  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. >> + * >> + *  Based on >> ArmPlatformPkg/Library/PL031RealTimeClockLib/PL031RealTimeClockLib.inf >> + * >> + **/ >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +STATIC CONST CHAR16  mEpochVariableName[]     = L"RtcEpochSeconds"; >> +STATIC CONST CHAR16  mTimeZoneVariableName[]  = L"RtcTimeZone"; >> +STATIC CONST CHAR16  mDaylightVariableName[]  = L"RtcDaylight"; >> + >> +/** >> +   Returns the current time and date information, and the >> time-keeping capabilities >> +   of the virtual RTC. >> + >> +   @param  Time                  A pointer to storage to receive a >> snapshot of the current time. >> +   @param  Capabilities          An optional pointer to a buffer to >> receive the real time clock >> +                                 device's capabilities. >> + >> +   @retval EFI_SUCCESS           The operation completed successfully. >> +   @retval EFI_INVALID_PARAMETER Time is NULL. >> +   @retval EFI_DEVICE_ERROR      The time could not be retrieved due >> to hardware error. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +LibGetTime ( >> +  OUT EFI_TIME               *Time, >> +  OUT EFI_TIME_CAPABILITIES  *Capabilities >> +  ) >> +{ >> +  EFI_STATUS  Status; >> +  UINT32      EpochSeconds; >> +  INT16       TimeZone; >> +  UINT8       Daylight; >> +  UINT64      Freq; >> +  UINT64      Counter; >> +  UINT64      Remainder; >> +  UINTN       ElapsedSeconds; >> +  UINTN       Size; >> + >> +  if (Time == NULL) { >> +    return EFI_INVALID_PARAMETER; >> +  } >> + >> +  // Get the counter frequency >> +  Freq = GetPerformanceCounterProperties (NULL, NULL); >> +  if (Freq == 0) { >> +    return EFI_DEVICE_ERROR; >> +  } >> + >> +  // Get the epoch time from non-volatile storage >> +  Size = sizeof (UINTN); >> +  ElapsedSeconds = 0; >> +  Status = EfiGetVariable ( >> +             (CHAR16 *)mEpochVariableName, >> +             &gEfiCallerIdGuid, >> +             NULL, >> +             &Size, >> +             (VOID *)&ElapsedSeconds >> +             ); >> +  // Fall back to compilation-time epoch if not set >> +  if (EFI_ERROR (Status)) { >> +    ASSERT(Status != EFI_INVALID_PARAMETER); >> +    ASSERT(Status != EFI_BUFFER_TOO_SMALL); >> +    // >> +    // The following is intended to produce a compilation error on build >> +    // environments where BUILD_EPOCH can not be set from inline shell. >> +    // If you are attempting to use this library on such an >> environment, please >> +    // contact the edk2 mailing list, so we can try to add support >> for it. >> +    // >> +    ElapsedSeconds = BUILD_EPOCH; >> +    DEBUG (( >> +      DEBUG_INFO, >> +      "LibGetTime: %s non volatile variable was not found - Using >> compilation time epoch.\n", >> +      mEpochVariableName >> +      )); >> +  } >> +  Counter = GetPerformanceCounter (); >> +  ElapsedSeconds += DivU64x64Remainder (Counter, Freq, &Remainder); >> + >> +  // Get the current time zone information from non-volatile storage >> +  Size = sizeof (TimeZone); >> +  Status = EfiGetVariable ( >> +             (CHAR16 *)mTimeZoneVariableName, >> +             &gEfiCallerIdGuid, >> +             NULL, >> +             &Size, >> +             (VOID *)&TimeZone >> +             ); >> + >> +  if (EFI_ERROR (Status)) { >> +    ASSERT(Status != EFI_INVALID_PARAMETER); >> +    ASSERT(Status != EFI_BUFFER_TOO_SMALL); >> + >> +    if (Status != EFI_NOT_FOUND) { >> +      return Status; >> +    } >> + >> +    // The time zone variable does not exist in non-volatile storage, >> so create it. >> +    Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE; >> +    // Store it >> +    Status = EfiSetVariable ( >> +               (CHAR16 *)mTimeZoneVariableName, >> +               &gEfiCallerIdGuid, >> +               EFI_VARIABLE_NON_VOLATILE | >> EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, >> +               Size, >> +               (VOID *)&(Time->TimeZone) >> +               ); >> +    if (EFI_ERROR (Status)) { >> +      DEBUG (( >> +        DEBUG_ERROR, >> +        "LibGetTime: Failed to save %s variable to non-volatile >> storage, Status = %r\n", >> +        mTimeZoneVariableName, >> +        Status >> +        )); >> +      return Status; >> +    } >> +  } else { >> +    // Got the time zone >> +    Time->TimeZone = TimeZone; >> + >> +    // Check TimeZone bounds: -1440 to 1440 or 2047 >> +    if (((Time->TimeZone < -1440) || (Time->TimeZone > 1440)) >> +        && (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE)) { >> +      Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE; >> +    } >> + >> +    // Adjust for the correct time zone >> +    if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) { >> +      EpochSeconds += Time->TimeZone * SEC_PER_MIN; >> +    } >> +  } >> + >> +  // Get the current daylight information from non-volatile storage >> +  Size = sizeof (Daylight); >> +  Status = EfiGetVariable ( >> +             (CHAR16 *)mDaylightVariableName, >> +             &gEfiCallerIdGuid, >> +             NULL, >> +             &Size, >> +             (VOID *)&Daylight >> +           ); >> + >> +  if (EFI_ERROR (Status)) { >> +    ASSERT(Status != EFI_INVALID_PARAMETER); >> +    ASSERT(Status != EFI_BUFFER_TOO_SMALL); >> + >> +    if (Status != EFI_NOT_FOUND) { >> +      return Status; >> +    } >> + >> +    // The daylight variable does not exist in non-volatile storage, >> so create it. >> +    Time->Daylight = 0; >> +    // Store it >> +    Status = EfiSetVariable ( >> +               (CHAR16 *)mDaylightVariableName, >> +               &gEfiCallerIdGuid, >> +               EFI_VARIABLE_NON_VOLATILE | >> EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, >> +               Size, >> +               (VOID *)&(Time->Daylight) >> +               ); >> +    if (EFI_ERROR (Status)) { >> +      DEBUG (( >> +        DEBUG_ERROR, >> +        "LibGetTime: Failed to save %s variable to non-volatile >> storage, Status = %r\n", >> +        mDaylightVariableName, >> +        Status >> +        )); >> +      return Status; >> +    } >> +  } else { >> +    // Got the daylight information >> +    Time->Daylight = Daylight; >> + >> +    // Adjust for the correct period >> +    if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == >> EFI_TIME_IN_DAYLIGHT) { >> +      // Convert to adjusted time, i.e. spring forwards one hour >> +      EpochSeconds += SEC_PER_HOUR; >> +    } >> +  } >> + >> +  EpochToEfiTime (ElapsedSeconds, Time); >> + >> +  // Because we use the performance counter, we can fill the >> Nanosecond attribute >> +  // provided that the remainder doesn't overflow 64-bit during >> multiplication. >> +  if (Remainder <= 18446744073U) { >> +    Time->Nanosecond = MultU64x64 (Remainder, 1000000000U) / Freq; >> +  } else { >> +    DEBUG ((DEBUG_WARN, "LibGetTime: Nanosecond value not set (64-bit >> overflow).\n")); >> +  } >> + >> +  if (Capabilities) { >> +    Capabilities->Accuracy   = 0; >> +    Capabilities->Resolution = Freq; >> +    Capabilities->SetsToZero = FALSE; >> +  } >> + >> +  return EFI_SUCCESS; >> +} >> + >> +/** >> +   Sets the current local time and date information. >> + >> +   @param  Time                  A pointer to the current time. >> + >> +   @retval EFI_SUCCESS           The operation completed successfully. >> +   @retval EFI_INVALID_PARAMETER A time field is out of range. >> +   @retval EFI_DEVICE_ERROR      The time could not be set due due to >> hardware error. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +LibSetTime ( >> +  IN EFI_TIME  *Time >> +  ) >> +{ >> +  EFI_STATUS  Status; >> +  UINTN       EpochSeconds; >> + >> +  if (!IsTimeValid (Time)) { >> +    return EFI_INVALID_PARAMETER; >> +  } >> + >> +  EpochSeconds = EfiTimeToEpoch (Time); >> + >> +  // Adjust for the correct time zone, i.e. convert to UTC time zone >> +  if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) { >> +    EpochSeconds -= Time->TimeZone * SEC_PER_MIN; >> +  } >> + >> +  // Adjust for the correct period >> +  if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) { >> +    // Convert to un-adjusted time, i.e. fall back one hour >> +    EpochSeconds -= SEC_PER_HOUR; >> +  } >> + >> +  // Save the current time zone information into non-volatile storage >> +  Status = EfiSetVariable ( >> +             (CHAR16 *)mTimeZoneVariableName, >> +             &gEfiCallerIdGuid, >> +             EFI_VARIABLE_NON_VOLATILE | >> EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, >> +             sizeof (Time->TimeZone), >> +             (VOID *)&(Time->TimeZone) >> +             ); >> +  if (EFI_ERROR (Status)) { >> +    DEBUG (( >> +      DEBUG_ERROR, >> +      "LibSetTime: Failed to save %s variable to non-volatile >> storage, Status = %r\n", >> +      mTimeZoneVariableName, >> +      Status >> +      )); >> +    return Status; >> +  } >> + >> +  // Save the current daylight information into non-volatile storage >> +  Status = EfiSetVariable ( >> +             (CHAR16 *)mDaylightVariableName, >> +             &gEfiCallerIdGuid, >> +             EFI_VARIABLE_NON_VOLATILE | >> EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, >> +             sizeof(Time->Daylight), >> +             (VOID *)&(Time->Daylight) >> +           ); >> +  if (EFI_ERROR (Status)) { >> +    DEBUG (( >> +      DEBUG_ERROR, >> +      "LibSetTime: Failed to save %s variable to non-volatile >> storage, Status = %r\n", >> +      mDaylightVariableName, >> +      Status >> +      )); >> +    return Status; >> +  } >> + >> +  Status = EfiSetVariable ( >> +             (CHAR16 *)mEpochVariableName, >> +             &gEfiCallerIdGuid, >> +             EFI_VARIABLE_NON_VOLATILE | >> EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, >> +             sizeof (EpochSeconds), >> +             &EpochSeconds >> +             ); >> +  if (EFI_ERROR (Status)) { >> +    DEBUG (( >> +      DEBUG_ERROR, >> +      "LibSetTime: Failed to save %s variable to non-volatile >> storage, Status = %r\n", >> +      mDaylightVariableName, >> +      Status >> +      )); >> +    return Status; >> +  } >> + >> +  return EFI_SUCCESS; >> +} >> + >> +/** >> +   Returns the current wakeup alarm clock setting. >> + >> +   @param  Enabled               Indicates if the alarm is currently >> enabled or disabled. >> +   @param  Pending               Indicates if the alarm signal is >> pending and requires acknowledgement. >> +   @param  Time                  The current alarm setting. >> + >> +   @retval EFI_SUCCESS           The alarm settings were returned. >> +   @retval EFI_INVALID_PARAMETER Any parameter is NULL. >> +   @retval EFI_DEVICE_ERROR      The wakeup time could not be >> retrieved due to a hardware error. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +LibGetWakeupTime ( >> +  OUT BOOLEAN   *Enabled, >> +  OUT BOOLEAN   *Pending, >> +  OUT EFI_TIME  *Time >> +  ) >> +{ >> +  return EFI_UNSUPPORTED; >> +} >> + >> +/** >> +   Sets the system wakeup alarm clock time. >> + >> +   @param  Enabled               Enable or disable the wakeup alarm. >> +   @param  Time                  If Enable is TRUE, the time to set >> the wakeup alarm for. >> + >> +   @retval EFI_SUCCESS           If Enable is TRUE, then the wakeup >> alarm was enabled. If >> +   Enable is FALSE, then the wakeup alarm was disabled. >> +   @retval EFI_INVALID_PARAMETER A time field is out of range. >> +   @retval EFI_DEVICE_ERROR      The wakeup time could not be set due >> to a hardware error. >> +   @retval EFI_UNSUPPORTED       A wakeup timer is not supported on >> this platform. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +LibSetWakeupTime ( >> +  IN  BOOLEAN   Enabled, >> +  OUT EFI_TIME  *Time >> +  ) >> +{ >> +  return EFI_UNSUPPORTED; >> +} >> + >> +/** >> +   This is the declaration of an EFI image entry point. This can be >> the entry point to an application >> +   written to this specification, an EFI boot service driver, or an >> EFI runtime driver. >> + >> +   @param  ImageHandle           Handle that identifies the loaded >> image. >> +   @param  SystemTable           System Table for this image. >> + >> +   @retval EFI_SUCCESS           The operation completed successfully. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +LibRtcInitialize ( >> +  IN EFI_HANDLE        ImageHandle, >> +  IN EFI_SYSTEM_TABLE  *SystemTable >> +  ) >> +{ >> +  return EFI_SUCCESS; >> +} >> + >> +/** >> +   Fixup internal data so that EFI can be call in virtual mode. >> +   Call the passed in Child Notify event and convert any pointers in >> +   lib to virtual mode. >> + >> +   @param[in]    Event   The Event that is being processed >> +   @param[in]    Context Event Context >> +**/ >> +VOID >> +EFIAPI >> +LibRtcVirtualNotifyEvent ( >> +  IN EFI_EVENT  Event, >> +  IN VOID       *Context >> +  ) >> +{ >> +  return; >> +} >> diff --git >> a/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.inf >> b/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.inf >> new file mode 100644 >> index 000000000000..ed69faad4205 >> --- /dev/null >> +++ >> b/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.inf >> @@ -0,0 +1,43 @@ >> +#/** @file >> +# >> +#  Implement virtual EFI RealTimeClock runtime services. >> +# >> +#  Copyright (c) 2019, Pete Batard >> +#  Copyright (c) 2018, Andrei Warkentin >> +#  Copyright (c) Microsoft Corporation. All rights reserved. > > Same issue here, 2019, alright? > >> +# >> +#  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. >> +# >> +#**/ >> + >> +[Defines] >> +  INF_VERSION                    = 0x0001001A >> +  BASE_NAME                      = VirtualRealTimeClockLib >> +  FILE_GUID                      = 1E27D461-78F3-4F7D-B1C2-F72384F13A6E >> +  MODULE_TYPE                    = BASE >> +  VERSION_STRING                 = 1.0 >> +  LIBRARY_CLASS                  = RealTimeClockLib >> + >> +[Sources.common] >> +  VirtualRealTimeClockLib.c >> + >> +[Packages] >> +  MdePkg/MdePkg.dec >> +  EmbeddedPkg/EmbeddedPkg.dec >> + >> +[LibraryClasses] >> +  IoLib >> +  DebugLib >> +  TimerLib >> +  TimeBaseLib >> +  UefiRuntimeLib >> + >> +# Current usage of this library expects GCC in a UNIX-like shell >> environment with the date command >> +[BuildOptions] >> +  GCC:*_*_*_CC_FLAGS = -DBUILD_EPOCH=`date +%s` >> >