/** @file | |
Generic Monotonic Counter services | |
Copyright (c) 2007, Intel Corporation. All rights reserved.<BR> | |
Portions copyright (c) 2008 - 2009, 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. | |
**/ | |
// | |
// The current Monotonic count value | |
// | |
UINT64 mEfiMtc = 0; | |
// | |
// Event to use to update the Mtc's high part when wrapping | |
// | |
EFI_EVENT mEfiMtcEvent; | |
// | |
// EfiMtcName - Variable name of the MTC value | |
// | |
CHAR16 *mEfiMtcName = L"MTC"; | |
// | |
// EfiMtcGuid - Guid of the MTC value | |
// | |
EFI_GUID mEfiMtcGuid = { 0xeb704011, 0x1402, 0x11d3, { 0x8e, 0x77, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b } }; | |
// | |
// Worker functions | |
// | |
VOID | |
EFIAPI | |
EfiMtcEventHandler ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
/*++ | |
Routine Description: | |
Monotonic count event handler. This handler updates the high monotonic count. | |
Arguments: | |
Event The event to handle | |
Context The event context | |
Returns: | |
EFI_SUCCESS The event has been handled properly | |
EFI_NOT_FOUND An error occurred updating the variable. | |
--*/ | |
{ | |
UINT32 HighCount; | |
EfiGetNextHighMonotonicCount (&HighCount); | |
return; | |
} | |
VOID | |
LibMtcVirtualAddressChangeEvent (VOID) | |
{ | |
} | |
EFI_STATUS | |
EFIAPI | |
LibMtcGetNextHighMonotonicCount ( | |
OUT UINT32 *HighCount | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_TPL OldTpl; | |
// | |
// Check input parameters | |
// | |
if (HighCount == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (!EfiAtRuntime ()) { | |
// Use a lock if called before ExitBootServices() | |
OldTpl = gBS->RaiseTPL (EFI_TPL_HIGH_LEVEL); | |
} | |
*HighCount = (UINT32) RShiftU64 (mEfiMtc, 32) + 1; | |
mEfiMtc = LShiftU64 (*HighCount, 32); | |
if (!EfiAtRuntime ()) { | |
gBS->RestoreTPL (OldTpl); | |
} | |
// | |
// Update the NvRam store to match the new high part | |
// | |
Status = EfiSetVariable ( | |
mEfiMtcName, | |
&mEfiMtcGuid, | |
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, | |
sizeof (UINT32), | |
HighCount | |
); | |
return Status; | |
} | |
EFI_STATUS | |
LibMtcGetNextMonotonicCount ( | |
OUT UINT64 *Count | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_TPL OldTpl; | |
UINT32 HighCount; | |
UINTN BufferSize; | |
// | |
// Can not be called after ExitBootServices() | |
// | |
if (EfiAtRuntime ()) { | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// Check input parameters | |
// | |
if (Count == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (mEfiMtc == 0) { | |
// | |
// If the MTC has not been initialized read the variable | |
// | |
// | |
// Read the last high part | |
// | |
BufferSize = sizeof (UINT32); | |
Status = EfiGetVariable ( | |
mEfiMtcName, | |
&mEfiMtcGuid, | |
NULL, | |
&BufferSize, | |
&HighCount | |
); | |
if (EFI_ERROR (Status)) { | |
HighCount = 0; | |
} | |
// | |
// Set the current value | |
// | |
mEfiMtc = LShiftU64 (HighCount, 32); | |
// | |
// Increment the upper 32 bits for this boot | |
// Continue even if it fails. It will only fail if the variable services are | |
// not functional. | |
// | |
Status = EfiGetNextHighMonotonicCount (&HighCount); | |
} | |
// | |
// Update the monotonic counter with a lock | |
// | |
OldTpl = gBS->RaiseTPL (EFI_TPL_HIGH_LEVEL); | |
*Count = mEfiMtc; | |
mEfiMtc++; | |
gBS->RestoreTPL (OldTpl); | |
// | |
// If the MSB bit of the low part toggled, then signal that the high | |
// part needs updated now | |
// | |
if ((((UINT32) mEfiMtc) ^ ((UINT32) *Count)) & 0x80000000) { | |
gBS->SignalEvent (mEfiMtcEvent); | |
} | |
return EFI_SUCCESS; | |
} | |
VOID | |
LibMtcInitialize ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
// | |
// Initialize event to handle overflows | |
// | |
Status = gBS->CreateEvent ( | |
EVT_NOTIFY_SIGNAL, | |
EFI_TPL_CALLBACK, | |
EfiMtcEventHandler, | |
NULL, | |
&mEfiMtcEvent | |
); | |
ASSERT_EFI_ERROR (Status); | |
} | |